sshconnect.c revision 294693
1/* $OpenBSD: sshconnect.c,v 1.246 2014/02/06 22:21:01 djm Exp $ */
2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5 *                    All rights reserved
6 * Code to connect to a remote host, and to perform the client side of the
7 * login (authentication) dialog.
8 *
9 * As far as I am concerned, the code I have written for this software
10 * can be used freely for any purpose.  Any derived versions of this
11 * software must be clearly marked as such, and if the derived work is
12 * incompatible with the protocol description in the RFC file, it must be
13 * called by a name other than "ssh" or "Secure Shell".
14 */
15
16#include "includes.h"
17__RCSID("$FreeBSD: stable/10/crypto/openssh/sshconnect.c 294693 2016-01-24 22:28:18Z des $");
18
19#include <sys/types.h>
20#include <sys/wait.h>
21#include <sys/stat.h>
22#include <sys/socket.h>
23#ifdef HAVE_SYS_TIME_H
24# include <sys/time.h>
25#endif
26
27#include <netinet/in.h>
28#include <arpa/inet.h>
29#include <rpc/rpc.h>
30
31#include <ctype.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <netdb.h>
35#ifdef HAVE_PATHS_H
36#include <paths.h>
37#endif
38#include <pwd.h>
39#include <signal.h>
40#include <stdarg.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45
46#include "xmalloc.h"
47#include "key.h"
48#include "hostfile.h"
49#include "ssh.h"
50#include "rsa.h"
51#include "buffer.h"
52#include "packet.h"
53#include "uidswap.h"
54#include "compat.h"
55#include "key.h"
56#include "sshconnect.h"
57#include "hostfile.h"
58#include "log.h"
59#include "readconf.h"
60#include "atomicio.h"
61#include "misc.h"
62#include "dns.h"
63#include "roaming.h"
64#include "monitor_fdpass.h"
65#include "ssh2.h"
66#include "version.h"
67
68char *client_version_string = NULL;
69char *server_version_string = NULL;
70
71static int matching_host_key_dns = 0;
72
73static pid_t proxy_command_pid = 0;
74
75/* import */
76extern Options options;
77extern char *__progname;
78extern uid_t original_real_uid;
79extern uid_t original_effective_uid;
80
81static int show_other_keys(struct hostkeys *, Key *);
82static void warn_changed_key(Key *);
83
84/* Expand a proxy command */
85static char *
86expand_proxy_command(const char *proxy_command, const char *user,
87    const char *host, int port)
88{
89	char *tmp, *ret, strport[NI_MAXSERV];
90
91	snprintf(strport, sizeof strport, "%d", port);
92	xasprintf(&tmp, "exec %s", proxy_command);
93	ret = percent_expand(tmp, "h", host, "p", strport,
94	    "r", options.user, (char *)NULL);
95	free(tmp);
96	return ret;
97}
98
99/*
100 * Connect to the given ssh server using a proxy command that passes a
101 * a connected fd back to us.
102 */
103static int
104ssh_proxy_fdpass_connect(const char *host, u_short port,
105    const char *proxy_command)
106{
107	char *command_string;
108	int sp[2], sock;
109	pid_t pid;
110	char *shell;
111
112	if ((shell = getenv("SHELL")) == NULL)
113		shell = _PATH_BSHELL;
114
115	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0)
116		fatal("Could not create socketpair to communicate with "
117		    "proxy dialer: %.100s", strerror(errno));
118
119	command_string = expand_proxy_command(proxy_command, options.user,
120	    host, port);
121	debug("Executing proxy dialer command: %.500s", command_string);
122
123	/* Fork and execute the proxy command. */
124	if ((pid = fork()) == 0) {
125		char *argv[10];
126
127		/* Child.  Permanently give up superuser privileges. */
128		permanently_drop_suid(original_real_uid);
129
130		close(sp[1]);
131		/* Redirect stdin and stdout. */
132		if (sp[0] != 0) {
133			if (dup2(sp[0], 0) < 0)
134				perror("dup2 stdin");
135		}
136		if (sp[0] != 1) {
137			if (dup2(sp[0], 1) < 0)
138				perror("dup2 stdout");
139		}
140		if (sp[0] >= 2)
141			close(sp[0]);
142
143		/*
144		 * Stderr is left as it is so that error messages get
145		 * printed on the user's terminal.
146		 */
147		argv[0] = shell;
148		argv[1] = "-c";
149		argv[2] = command_string;
150		argv[3] = NULL;
151
152		/*
153		 * Execute the proxy command.
154		 * Note that we gave up any extra privileges above.
155		 */
156		execv(argv[0], argv);
157		perror(argv[0]);
158		exit(1);
159	}
160	/* Parent. */
161	if (pid < 0)
162		fatal("fork failed: %.100s", strerror(errno));
163	close(sp[0]);
164	free(command_string);
165
166	if ((sock = mm_receive_fd(sp[1])) == -1)
167		fatal("proxy dialer did not pass back a connection");
168
169	while (waitpid(pid, NULL, 0) == -1)
170		if (errno != EINTR)
171			fatal("Couldn't wait for child: %s", strerror(errno));
172
173	/* Set the connection file descriptors. */
174	packet_set_connection(sock, sock);
175
176	return 0;
177}
178
179/*
180 * Connect to the given ssh server using a proxy command.
181 */
182static int
183ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
184{
185	char *command_string;
186	int pin[2], pout[2];
187	pid_t pid;
188	char *shell;
189
190	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
191		shell = _PATH_BSHELL;
192
193	/* Create pipes for communicating with the proxy. */
194	if (pipe(pin) < 0 || pipe(pout) < 0)
195		fatal("Could not create pipes to communicate with the proxy: %.100s",
196		    strerror(errno));
197
198	command_string = expand_proxy_command(proxy_command, options.user,
199	    host, port);
200	debug("Executing proxy command: %.500s", command_string);
201
202	/* Fork and execute the proxy command. */
203	if ((pid = fork()) == 0) {
204		char *argv[10];
205
206		/* Child.  Permanently give up superuser privileges. */
207		permanently_drop_suid(original_real_uid);
208
209		/* Redirect stdin and stdout. */
210		close(pin[1]);
211		if (pin[0] != 0) {
212			if (dup2(pin[0], 0) < 0)
213				perror("dup2 stdin");
214			close(pin[0]);
215		}
216		close(pout[0]);
217		if (dup2(pout[1], 1) < 0)
218			perror("dup2 stdout");
219		/* Cannot be 1 because pin allocated two descriptors. */
220		close(pout[1]);
221
222		/* Stderr is left as it is so that error messages get
223		   printed on the user's terminal. */
224		argv[0] = shell;
225		argv[1] = "-c";
226		argv[2] = command_string;
227		argv[3] = NULL;
228
229		/* Execute the proxy command.  Note that we gave up any
230		   extra privileges above. */
231		signal(SIGPIPE, SIG_DFL);
232		execv(argv[0], argv);
233		perror(argv[0]);
234		exit(1);
235	}
236	/* Parent. */
237	if (pid < 0)
238		fatal("fork failed: %.100s", strerror(errno));
239	else
240		proxy_command_pid = pid; /* save pid to clean up later */
241
242	/* Close child side of the descriptors. */
243	close(pin[0]);
244	close(pout[1]);
245
246	/* Free the command name. */
247	free(command_string);
248
249	/* Set the connection file descriptors. */
250	packet_set_connection(pout[0], pin[1]);
251
252	/* Indicate OK return */
253	return 0;
254}
255
256void
257ssh_kill_proxy_command(void)
258{
259	/*
260	 * Send SIGHUP to proxy command if used. We don't wait() in
261	 * case it hangs and instead rely on init to reap the child
262	 */
263	if (proxy_command_pid > 1)
264		kill(proxy_command_pid, SIGHUP);
265}
266
267/*
268 * Creates a (possibly privileged) socket for use as the ssh connection.
269 */
270static int
271ssh_create_socket(int privileged, struct addrinfo *ai)
272{
273	int sock, r, gaierr;
274	struct addrinfo hints, *res = NULL;
275
276	sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
277	if (sock < 0) {
278		error("socket: %s", strerror(errno));
279		return -1;
280	}
281	fcntl(sock, F_SETFD, FD_CLOEXEC);
282
283	/* Bind the socket to an alternative local IP address */
284	if (options.bind_address == NULL && !privileged)
285		return sock;
286
287	if (options.bind_address) {
288		memset(&hints, 0, sizeof(hints));
289		hints.ai_family = ai->ai_family;
290		hints.ai_socktype = ai->ai_socktype;
291		hints.ai_protocol = ai->ai_protocol;
292		hints.ai_flags = AI_PASSIVE;
293		gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res);
294		if (gaierr) {
295			error("getaddrinfo: %s: %s", options.bind_address,
296			    ssh_gai_strerror(gaierr));
297			close(sock);
298			return -1;
299		}
300	}
301	/*
302	 * If we are running as root and want to connect to a privileged
303	 * port, bind our own socket to a privileged port.
304	 */
305	if (privileged) {
306		PRIV_START;
307		r = bindresvport_sa(sock, res ? res->ai_addr : NULL);
308		PRIV_END;
309		if (r < 0) {
310			error("bindresvport_sa: af=%d %s", ai->ai_family,
311			    strerror(errno));
312			goto fail;
313		}
314	} else {
315		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
316			error("bind: %s: %s", options.bind_address,
317			    strerror(errno));
318 fail:
319			close(sock);
320			freeaddrinfo(res);
321			return -1;
322		}
323	}
324	if (res != NULL)
325		freeaddrinfo(res);
326	return sock;
327}
328
329static int
330timeout_connect(int sockfd, const struct sockaddr *serv_addr,
331    socklen_t addrlen, int *timeoutp)
332{
333	fd_set *fdset;
334	struct timeval tv, t_start;
335	socklen_t optlen;
336	int optval, rc, result = -1;
337
338	gettimeofday(&t_start, NULL);
339
340	if (*timeoutp <= 0) {
341		result = connect(sockfd, serv_addr, addrlen);
342		goto done;
343	}
344
345	set_nonblock(sockfd);
346	rc = connect(sockfd, serv_addr, addrlen);
347	if (rc == 0) {
348		unset_nonblock(sockfd);
349		result = 0;
350		goto done;
351	}
352	if (errno != EINPROGRESS) {
353		result = -1;
354		goto done;
355	}
356
357	fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS),
358	    sizeof(fd_mask));
359	FD_SET(sockfd, fdset);
360	ms_to_timeval(&tv, *timeoutp);
361
362	for (;;) {
363		rc = select(sockfd + 1, NULL, fdset, NULL, &tv);
364		if (rc != -1 || errno != EINTR)
365			break;
366	}
367
368	switch (rc) {
369	case 0:
370		/* Timed out */
371		errno = ETIMEDOUT;
372		break;
373	case -1:
374		/* Select error */
375		debug("select: %s", strerror(errno));
376		break;
377	case 1:
378		/* Completed or failed */
379		optval = 0;
380		optlen = sizeof(optval);
381		if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval,
382		    &optlen) == -1) {
383			debug("getsockopt: %s", strerror(errno));
384			break;
385		}
386		if (optval != 0) {
387			errno = optval;
388			break;
389		}
390		result = 0;
391		unset_nonblock(sockfd);
392		break;
393	default:
394		/* Should not occur */
395		fatal("Bogus return (%d) from select()", rc);
396	}
397
398	free(fdset);
399
400 done:
401 	if (result == 0 && *timeoutp > 0) {
402		ms_subtract_diff(&t_start, timeoutp);
403		if (*timeoutp <= 0) {
404			errno = ETIMEDOUT;
405			result = -1;
406		}
407	}
408
409	return (result);
410}
411
412/*
413 * Opens a TCP/IP connection to the remote server on the given host.
414 * The address of the remote host will be returned in hostaddr.
415 * If port is 0, the default port will be used.  If needpriv is true,
416 * a privileged port will be allocated to make the connection.
417 * This requires super-user privileges if needpriv is true.
418 * Connection_attempts specifies the maximum number of tries (one per
419 * second).  If proxy_command is non-NULL, it specifies the command (with %h
420 * and %p substituted for host and port, respectively) to use to contact
421 * the daemon.
422 */
423static int
424ssh_connect_direct(const char *host, struct addrinfo *aitop,
425    struct sockaddr_storage *hostaddr, u_short port, int family,
426    int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
427{
428	int on = 1;
429	int sock = -1, attempt;
430	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
431	struct addrinfo *ai;
432
433	debug2("ssh_connect: needpriv %d", needpriv);
434
435	for (attempt = 0; attempt < connection_attempts; attempt++) {
436		if (attempt > 0) {
437			/* Sleep a moment before retrying. */
438			sleep(1);
439			debug("Trying again...");
440		}
441		/*
442		 * Loop through addresses for this host, and try each one in
443		 * sequence until the connection succeeds.
444		 */
445		for (ai = aitop; ai; ai = ai->ai_next) {
446			if (ai->ai_family != AF_INET &&
447			    ai->ai_family != AF_INET6)
448				continue;
449			if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
450			    ntop, sizeof(ntop), strport, sizeof(strport),
451			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
452				error("ssh_connect: getnameinfo failed");
453				continue;
454			}
455			debug("Connecting to %.200s [%.100s] port %s.",
456				host, ntop, strport);
457
458			/* Create a socket for connecting. */
459			sock = ssh_create_socket(needpriv, ai);
460			if (sock < 0)
461				/* Any error is already output */
462				continue;
463
464			if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
465			    timeout_ms) >= 0) {
466				/* Successful connection. */
467				memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
468				break;
469			} else {
470				debug("connect to address %s port %s: %s",
471				    ntop, strport, strerror(errno));
472				close(sock);
473				sock = -1;
474			}
475		}
476		if (sock != -1)
477			break;	/* Successful connection. */
478	}
479
480	/* Return failure if we didn't get a successful connection. */
481	if (sock == -1) {
482		error("ssh: connect to host %s port %s: %s",
483		    host, strport, strerror(errno));
484		return (-1);
485	}
486
487	debug("Connection established.");
488
489	/* Set SO_KEEPALIVE if requested. */
490	if (want_keepalive &&
491	    setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
492	    sizeof(on)) < 0)
493		error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
494
495	/* Set the connection. */
496	packet_set_connection(sock, sock);
497
498	return 0;
499}
500
501int
502ssh_connect(const char *host, struct addrinfo *addrs,
503    struct sockaddr_storage *hostaddr, u_short port, int family,
504    int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv)
505{
506	if (options.proxy_command == NULL) {
507		return ssh_connect_direct(host, addrs, hostaddr, port, family,
508		    connection_attempts, timeout_ms, want_keepalive, needpriv);
509	} else if (strcmp(options.proxy_command, "-") == 0) {
510		packet_set_connection(STDIN_FILENO, STDOUT_FILENO);
511		return 0; /* Always succeeds */
512	} else if (options.proxy_use_fdpass) {
513		return ssh_proxy_fdpass_connect(host, port,
514		    options.proxy_command);
515	}
516	return ssh_proxy_connect(host, port, options.proxy_command);
517}
518
519static void
520send_client_banner(int connection_out, int minor1)
521{
522	/* Send our own protocol version identification. */
523	xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s%s",
524	    compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
525	    compat20 ? PROTOCOL_MINOR_2 : minor1,
526	    SSH_VERSION,
527	    *options.version_addendum == '\0' ? "" : " ",
528	    options.version_addendum, compat20 ? "\r\n" : "\n");
529	if (roaming_atomicio(vwrite, connection_out, client_version_string,
530	    strlen(client_version_string)) != strlen(client_version_string))
531		fatal("write: %.100s", strerror(errno));
532	chop(client_version_string);
533	debug("Local version string %.100s", client_version_string);
534}
535
536/*
537 * Waits for the server identification string, and sends our own
538 * identification string.
539 */
540void
541ssh_exchange_identification(int timeout_ms)
542{
543	char buf[256], remote_version[256];	/* must be same size! */
544	int remote_major, remote_minor, mismatch;
545	int connection_in = packet_get_connection_in();
546	int connection_out = packet_get_connection_out();
547	int minor1 = PROTOCOL_MINOR_1, client_banner_sent = 0;
548	u_int i, n;
549	size_t len;
550	int fdsetsz, remaining, rc;
551	struct timeval t_start, t_remaining;
552	fd_set *fdset;
553
554	fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask);
555	fdset = xcalloc(1, fdsetsz);
556
557	/*
558	 * If we are SSH2-only then we can send the banner immediately and
559	 * save a round-trip.
560	 */
561	if (options.protocol == SSH_PROTO_2) {
562		enable_compat20();
563		send_client_banner(connection_out, 0);
564		client_banner_sent = 1;
565	}
566
567	/* Read other side's version identification. */
568	remaining = timeout_ms;
569	for (n = 0;;) {
570		for (i = 0; i < sizeof(buf) - 1; i++) {
571			if (timeout_ms > 0) {
572				gettimeofday(&t_start, NULL);
573				ms_to_timeval(&t_remaining, remaining);
574				FD_SET(connection_in, fdset);
575				rc = select(connection_in + 1, fdset, NULL,
576				    fdset, &t_remaining);
577				ms_subtract_diff(&t_start, &remaining);
578				if (rc == 0 || remaining <= 0)
579					fatal("Connection timed out during "
580					    "banner exchange");
581				if (rc == -1) {
582					if (errno == EINTR)
583						continue;
584					fatal("ssh_exchange_identification: "
585					    "select: %s", strerror(errno));
586				}
587			}
588
589			len = roaming_atomicio(read, connection_in, &buf[i], 1);
590
591			if (len != 1 && errno == EPIPE)
592				fatal("ssh_exchange_identification: "
593				    "Connection closed by remote host");
594			else if (len != 1)
595				fatal("ssh_exchange_identification: "
596				    "read: %.100s", strerror(errno));
597			if (buf[i] == '\r') {
598				buf[i] = '\n';
599				buf[i + 1] = 0;
600				continue;		/**XXX wait for \n */
601			}
602			if (buf[i] == '\n') {
603				buf[i + 1] = 0;
604				break;
605			}
606			if (++n > 65536)
607				fatal("ssh_exchange_identification: "
608				    "No banner received");
609		}
610		buf[sizeof(buf) - 1] = 0;
611		if (strncmp(buf, "SSH-", 4) == 0)
612			break;
613		debug("ssh_exchange_identification: %s", buf);
614	}
615	server_version_string = xstrdup(buf);
616	free(fdset);
617
618	/*
619	 * Check that the versions match.  In future this might accept
620	 * several versions and set appropriate flags to handle them.
621	 */
622	if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n",
623	    &remote_major, &remote_minor, remote_version) != 3)
624		fatal("Bad remote protocol version identification: '%.100s'", buf);
625	debug("Remote protocol version %d.%d, remote software version %.100s",
626	    remote_major, remote_minor, remote_version);
627
628	compat_datafellows(remote_version);
629	mismatch = 0;
630
631	switch (remote_major) {
632	case 1:
633		if (remote_minor == 99 &&
634		    (options.protocol & SSH_PROTO_2) &&
635		    !(options.protocol & SSH_PROTO_1_PREFERRED)) {
636			enable_compat20();
637			break;
638		}
639		if (!(options.protocol & SSH_PROTO_1)) {
640			mismatch = 1;
641			break;
642		}
643		if (remote_minor < 3) {
644			fatal("Remote machine has too old SSH software version.");
645		} else if (remote_minor == 3 || remote_minor == 4) {
646			/* We speak 1.3, too. */
647			enable_compat13();
648			minor1 = 3;
649			if (options.forward_agent) {
650				logit("Agent forwarding disabled for protocol 1.3");
651				options.forward_agent = 0;
652			}
653		}
654		break;
655	case 2:
656		if (options.protocol & SSH_PROTO_2) {
657			enable_compat20();
658			break;
659		}
660		/* FALLTHROUGH */
661	default:
662		mismatch = 1;
663		break;
664	}
665	if (mismatch)
666		fatal("Protocol major versions differ: %d vs. %d",
667		    (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
668		    remote_major);
669	if ((datafellows & SSH_BUG_DERIVEKEY) != 0)
670		fatal("Server version \"%.100s\" uses unsafe key agreement; "
671		    "refusing connection", remote_version);
672	if ((datafellows & SSH_BUG_RSASIGMD5) != 0)
673		logit("Server version \"%.100s\" uses unsafe RSA signature "
674		    "scheme; disabling use of RSA keys", remote_version);
675	if (!client_banner_sent)
676		send_client_banner(connection_out, minor1);
677	chop(server_version_string);
678}
679
680/* defaults to 'no' */
681static int
682confirm(const char *prompt)
683{
684	const char *msg, *again = "Please type 'yes' or 'no': ";
685	char *p;
686	int ret = -1;
687
688	if (options.batch_mode)
689		return 0;
690	for (msg = prompt;;msg = again) {
691		p = read_passphrase(msg, RP_ECHO);
692		if (p == NULL ||
693		    (p[0] == '\0') || (p[0] == '\n') ||
694		    strncasecmp(p, "no", 2) == 0)
695			ret = 0;
696		if (p && strncasecmp(p, "yes", 3) == 0)
697			ret = 1;
698		free(p);
699		if (ret != -1)
700			return ret;
701	}
702}
703
704static int
705check_host_cert(const char *host, const Key *host_key)
706{
707	const char *reason;
708
709	if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) {
710		error("%s", reason);
711		return 0;
712	}
713	if (buffer_len(&host_key->cert->critical) != 0) {
714		error("Certificate for %s contains unsupported "
715		    "critical options(s)", host);
716		return 0;
717	}
718	return 1;
719}
720
721static int
722sockaddr_is_local(struct sockaddr *hostaddr)
723{
724	switch (hostaddr->sa_family) {
725	case AF_INET:
726		return (ntohl(((struct sockaddr_in *)hostaddr)->
727		    sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
728	case AF_INET6:
729		return IN6_IS_ADDR_LOOPBACK(
730		    &(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
731	default:
732		return 0;
733	}
734}
735
736/*
737 * Prepare the hostname and ip address strings that are used to lookup
738 * host keys in known_hosts files. These may have a port number appended.
739 */
740void
741get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr,
742    u_short port, char **hostfile_hostname, char **hostfile_ipaddr)
743{
744	char ntop[NI_MAXHOST];
745	socklen_t addrlen;
746
747	switch (hostaddr == NULL ? -1 : hostaddr->sa_family) {
748	case -1:
749		addrlen = 0;
750		break;
751	case AF_INET:
752		addrlen = sizeof(struct sockaddr_in);
753		break;
754	case AF_INET6:
755		addrlen = sizeof(struct sockaddr_in6);
756		break;
757	default:
758		addrlen = sizeof(struct sockaddr);
759		break;
760	}
761
762	/*
763	 * We don't have the remote ip-address for connections
764	 * using a proxy command
765	 */
766	if (hostfile_ipaddr != NULL) {
767		if (options.proxy_command == NULL) {
768			if (getnameinfo(hostaddr, addrlen,
769			    ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0)
770			fatal("check_host_key: getnameinfo failed");
771			*hostfile_ipaddr = put_host_port(ntop, port);
772		} else {
773			*hostfile_ipaddr = xstrdup("<no hostip for proxy "
774			    "command>");
775		}
776	}
777
778	/*
779	 * Allow the user to record the key under a different name or
780	 * differentiate a non-standard port.  This is useful for ssh
781	 * tunneling over forwarded connections or if you run multiple
782	 * sshd's on different ports on the same machine.
783	 */
784	if (hostfile_hostname != NULL) {
785		if (options.host_key_alias != NULL) {
786			*hostfile_hostname = xstrdup(options.host_key_alias);
787			debug("using hostkeyalias: %s", *hostfile_hostname);
788		} else {
789			*hostfile_hostname = put_host_port(hostname, port);
790		}
791	}
792}
793
794/*
795 * check whether the supplied host key is valid, return -1 if the key
796 * is not valid. user_hostfile[0] will not be updated if 'readonly' is true.
797 */
798#define RDRW	0
799#define RDONLY	1
800#define ROQUIET	2
801static int
802check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
803    Key *host_key, int readonly,
804    char **user_hostfiles, u_int num_user_hostfiles,
805    char **system_hostfiles, u_int num_system_hostfiles)
806{
807	HostStatus host_status;
808	HostStatus ip_status;
809	Key *raw_key = NULL;
810	char *ip = NULL, *host = NULL;
811	char hostline[1000], *hostp, *fp, *ra;
812	char msg[1024];
813	const char *type;
814	const struct hostkey_entry *host_found, *ip_found;
815	int len, cancelled_forwarding = 0;
816	int local = sockaddr_is_local(hostaddr);
817	int r, want_cert = key_is_cert(host_key), host_ip_differ = 0;
818	struct hostkeys *host_hostkeys, *ip_hostkeys;
819	u_int i;
820
821	/*
822	 * Force accepting of the host key for loopback/localhost. The
823	 * problem is that if the home directory is NFS-mounted to multiple
824	 * machines, localhost will refer to a different machine in each of
825	 * them, and the user will get bogus HOST_CHANGED warnings.  This
826	 * essentially disables host authentication for localhost; however,
827	 * this is probably not a real problem.
828	 */
829	if (options.no_host_authentication_for_localhost == 1 && local &&
830	    options.host_key_alias == NULL) {
831		debug("Forcing accepting of host key for "
832		    "loopback/localhost.");
833		return 0;
834	}
835
836	/*
837	 * Prepare the hostname and address strings used for hostkey lookup.
838	 * In some cases, these will have a port number appended.
839	 */
840	get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip);
841
842	/*
843	 * Turn off check_host_ip if the connection is to localhost, via proxy
844	 * command or if we don't have a hostname to compare with
845	 */
846	if (options.check_host_ip && (local ||
847	    strcmp(hostname, ip) == 0 || options.proxy_command != NULL))
848		options.check_host_ip = 0;
849
850	host_hostkeys = init_hostkeys();
851	for (i = 0; i < num_user_hostfiles; i++)
852		load_hostkeys(host_hostkeys, host, user_hostfiles[i]);
853	for (i = 0; i < num_system_hostfiles; i++)
854		load_hostkeys(host_hostkeys, host, system_hostfiles[i]);
855
856	ip_hostkeys = NULL;
857	if (!want_cert && options.check_host_ip) {
858		ip_hostkeys = init_hostkeys();
859		for (i = 0; i < num_user_hostfiles; i++)
860			load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]);
861		for (i = 0; i < num_system_hostfiles; i++)
862			load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]);
863	}
864
865 retry:
866	/* Reload these as they may have changed on cert->key downgrade */
867	want_cert = key_is_cert(host_key);
868	type = key_type(host_key);
869
870	/*
871	 * Check if the host key is present in the user's list of known
872	 * hosts or in the systemwide list.
873	 */
874	host_status = check_key_in_hostkeys(host_hostkeys, host_key,
875	    &host_found);
876
877	/*
878	 * Also perform check for the ip address, skip the check if we are
879	 * localhost, looking for a certificate, or the hostname was an ip
880	 * address to begin with.
881	 */
882	if (!want_cert && ip_hostkeys != NULL) {
883		ip_status = check_key_in_hostkeys(ip_hostkeys, host_key,
884		    &ip_found);
885		if (host_status == HOST_CHANGED &&
886		    (ip_status != HOST_CHANGED ||
887		    (ip_found != NULL &&
888		    !key_equal(ip_found->key, host_found->key))))
889			host_ip_differ = 1;
890	} else
891		ip_status = host_status;
892
893	switch (host_status) {
894	case HOST_OK:
895		/* The host is known and the key matches. */
896		debug("Host '%.200s' is known and matches the %s host %s.",
897		    host, type, want_cert ? "certificate" : "key");
898		debug("Found %s in %s:%lu", want_cert ? "CA key" : "key",
899		    host_found->file, host_found->line);
900		if (want_cert && !check_host_cert(hostname, host_key))
901			goto fail;
902		if (options.check_host_ip && ip_status == HOST_NEW) {
903			if (readonly || want_cert)
904				logit("%s host key for IP address "
905				    "'%.128s' not in list of known hosts.",
906				    type, ip);
907			else if (!add_host_to_hostfile(user_hostfiles[0], ip,
908			    host_key, options.hash_known_hosts))
909				logit("Failed to add the %s host key for IP "
910				    "address '%.128s' to the list of known "
911				    "hosts (%.30s).", type, ip,
912				    user_hostfiles[0]);
913			else
914				logit("Warning: Permanently added the %s host "
915				    "key for IP address '%.128s' to the list "
916				    "of known hosts.", type, ip);
917		} else if (options.visual_host_key) {
918			fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
919			ra = key_fingerprint(host_key, SSH_FP_MD5,
920			    SSH_FP_RANDOMART);
921			logit("Host key fingerprint is %s\n%s\n", fp, ra);
922			free(ra);
923			free(fp);
924		}
925		break;
926	case HOST_NEW:
927		if (options.host_key_alias == NULL && port != 0 &&
928		    port != SSH_DEFAULT_PORT) {
929			debug("checking without port identifier");
930			if (check_host_key(hostname, hostaddr, 0, host_key,
931			    ROQUIET, user_hostfiles, num_user_hostfiles,
932			    system_hostfiles, num_system_hostfiles) == 0) {
933				debug("found matching key w/out port");
934				break;
935			}
936		}
937		if (readonly || want_cert)
938			goto fail;
939		/* The host is new. */
940		if (options.strict_host_key_checking == 1) {
941			/*
942			 * User has requested strict host key checking.  We
943			 * will not add the host key automatically.  The only
944			 * alternative left is to abort.
945			 */
946			error("No %s host key is known for %.200s and you "
947			    "have requested strict checking.", type, host);
948			goto fail;
949		} else if (options.strict_host_key_checking == 2) {
950			char msg1[1024], msg2[1024];
951
952			if (show_other_keys(host_hostkeys, host_key))
953				snprintf(msg1, sizeof(msg1),
954				    "\nbut keys of different type are already"
955				    " known for this host.");
956			else
957				snprintf(msg1, sizeof(msg1), ".");
958			/* The default */
959			fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
960			ra = key_fingerprint(host_key, SSH_FP_MD5,
961			    SSH_FP_RANDOMART);
962			msg2[0] = '\0';
963			if (options.verify_host_key_dns) {
964				if (matching_host_key_dns)
965					snprintf(msg2, sizeof(msg2),
966					    "Matching host key fingerprint"
967					    " found in DNS.\n");
968				else
969					snprintf(msg2, sizeof(msg2),
970					    "No matching host key fingerprint"
971					    " found in DNS.\n");
972			}
973			snprintf(msg, sizeof(msg),
974			    "The authenticity of host '%.200s (%s)' can't be "
975			    "established%s\n"
976			    "%s key fingerprint is %s.%s%s\n%s"
977			    "Are you sure you want to continue connecting "
978			    "(yes/no)? ",
979			    host, ip, msg1, type, fp,
980			    options.visual_host_key ? "\n" : "",
981			    options.visual_host_key ? ra : "",
982			    msg2);
983			free(ra);
984			free(fp);
985			if (!confirm(msg))
986				goto fail;
987		}
988		/*
989		 * If not in strict mode, add the key automatically to the
990		 * local known_hosts file.
991		 */
992		if (options.check_host_ip && ip_status == HOST_NEW) {
993			snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
994			hostp = hostline;
995			if (options.hash_known_hosts) {
996				/* Add hash of host and IP separately */
997				r = add_host_to_hostfile(user_hostfiles[0],
998				    host, host_key, options.hash_known_hosts) &&
999				    add_host_to_hostfile(user_hostfiles[0], ip,
1000				    host_key, options.hash_known_hosts);
1001			} else {
1002				/* Add unhashed "host,ip" */
1003				r = add_host_to_hostfile(user_hostfiles[0],
1004				    hostline, host_key,
1005				    options.hash_known_hosts);
1006			}
1007		} else {
1008			r = add_host_to_hostfile(user_hostfiles[0], host,
1009			    host_key, options.hash_known_hosts);
1010			hostp = host;
1011		}
1012
1013		if (!r)
1014			logit("Failed to add the host to the list of known "
1015			    "hosts (%.500s).", user_hostfiles[0]);
1016		else
1017			logit("Warning: Permanently added '%.200s' (%s) to the "
1018			    "list of known hosts.", hostp, type);
1019		break;
1020	case HOST_REVOKED:
1021		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1022		error("@       WARNING: REVOKED HOST KEY DETECTED!               @");
1023		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1024		error("The %s host key for %s is marked as revoked.", type, host);
1025		error("This could mean that a stolen key is being used to");
1026		error("impersonate this host.");
1027
1028		/*
1029		 * If strict host key checking is in use, the user will have
1030		 * to edit the key manually and we can only abort.
1031		 */
1032		if (options.strict_host_key_checking) {
1033			error("%s host key for %.200s was revoked and you have "
1034			    "requested strict checking.", type, host);
1035			goto fail;
1036		}
1037		goto continue_unsafe;
1038
1039	case HOST_CHANGED:
1040		if (want_cert) {
1041			/*
1042			 * This is only a debug() since it is valid to have
1043			 * CAs with wildcard DNS matches that don't match
1044			 * all hosts that one might visit.
1045			 */
1046			debug("Host certificate authority does not "
1047			    "match %s in %s:%lu", CA_MARKER,
1048			    host_found->file, host_found->line);
1049			goto fail;
1050		}
1051		if (readonly == ROQUIET)
1052			goto fail;
1053		if (options.check_host_ip && host_ip_differ) {
1054			char *key_msg;
1055			if (ip_status == HOST_NEW)
1056				key_msg = "is unknown";
1057			else if (ip_status == HOST_OK)
1058				key_msg = "is unchanged";
1059			else
1060				key_msg = "has a different value";
1061			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1062			error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
1063			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1064			error("The %s host key for %s has changed,", type, host);
1065			error("and the key for the corresponding IP address %s", ip);
1066			error("%s. This could either mean that", key_msg);
1067			error("DNS SPOOFING is happening or the IP address for the host");
1068			error("and its host key have changed at the same time.");
1069			if (ip_status != HOST_NEW)
1070				error("Offending key for IP in %s:%lu",
1071				    ip_found->file, ip_found->line);
1072		}
1073		/* The host key has changed. */
1074		warn_changed_key(host_key);
1075		error("Add correct host key in %.100s to get rid of this message.",
1076		    user_hostfiles[0]);
1077		error("Offending %s key in %s:%lu", key_type(host_found->key),
1078		    host_found->file, host_found->line);
1079
1080		/*
1081		 * If strict host key checking is in use, the user will have
1082		 * to edit the key manually and we can only abort.
1083		 */
1084		if (options.strict_host_key_checking) {
1085			error("%s host key for %.200s has changed and you have "
1086			    "requested strict checking.", type, host);
1087			goto fail;
1088		}
1089
1090 continue_unsafe:
1091		/*
1092		 * If strict host key checking has not been requested, allow
1093		 * the connection but without MITM-able authentication or
1094		 * forwarding.
1095		 */
1096		if (options.password_authentication) {
1097			error("Password authentication is disabled to avoid "
1098			    "man-in-the-middle attacks.");
1099			options.password_authentication = 0;
1100			cancelled_forwarding = 1;
1101		}
1102		if (options.kbd_interactive_authentication) {
1103			error("Keyboard-interactive authentication is disabled"
1104			    " to avoid man-in-the-middle attacks.");
1105			options.kbd_interactive_authentication = 0;
1106			options.challenge_response_authentication = 0;
1107			cancelled_forwarding = 1;
1108		}
1109		if (options.challenge_response_authentication) {
1110			error("Challenge/response authentication is disabled"
1111			    " to avoid man-in-the-middle attacks.");
1112			options.challenge_response_authentication = 0;
1113			cancelled_forwarding = 1;
1114		}
1115		if (options.forward_agent) {
1116			error("Agent forwarding is disabled to avoid "
1117			    "man-in-the-middle attacks.");
1118			options.forward_agent = 0;
1119			cancelled_forwarding = 1;
1120		}
1121		if (options.forward_x11) {
1122			error("X11 forwarding is disabled to avoid "
1123			    "man-in-the-middle attacks.");
1124			options.forward_x11 = 0;
1125			cancelled_forwarding = 1;
1126		}
1127		if (options.num_local_forwards > 0 ||
1128		    options.num_remote_forwards > 0) {
1129			error("Port forwarding is disabled to avoid "
1130			    "man-in-the-middle attacks.");
1131			options.num_local_forwards =
1132			    options.num_remote_forwards = 0;
1133			cancelled_forwarding = 1;
1134		}
1135		if (options.tun_open != SSH_TUNMODE_NO) {
1136			error("Tunnel forwarding is disabled to avoid "
1137			    "man-in-the-middle attacks.");
1138			options.tun_open = SSH_TUNMODE_NO;
1139			cancelled_forwarding = 1;
1140		}
1141		if (options.exit_on_forward_failure && cancelled_forwarding)
1142			fatal("Error: forwarding disabled due to host key "
1143			    "check failure");
1144
1145		/*
1146		 * XXX Should permit the user to change to use the new id.
1147		 * This could be done by converting the host key to an
1148		 * identifying sentence, tell that the host identifies itself
1149		 * by that sentence, and ask the user if he/she wishes to
1150		 * accept the authentication.
1151		 */
1152		break;
1153	case HOST_FOUND:
1154		fatal("internal error");
1155		break;
1156	}
1157
1158	if (options.check_host_ip && host_status != HOST_CHANGED &&
1159	    ip_status == HOST_CHANGED) {
1160		snprintf(msg, sizeof(msg),
1161		    "Warning: the %s host key for '%.200s' "
1162		    "differs from the key for the IP address '%.128s'"
1163		    "\nOffending key for IP in %s:%lu",
1164		    type, host, ip, ip_found->file, ip_found->line);
1165		if (host_status == HOST_OK) {
1166			len = strlen(msg);
1167			snprintf(msg + len, sizeof(msg) - len,
1168			    "\nMatching host key in %s:%lu",
1169			    host_found->file, host_found->line);
1170		}
1171		if (options.strict_host_key_checking == 1) {
1172			logit("%s", msg);
1173			error("Exiting, you have requested strict checking.");
1174			goto fail;
1175		} else if (options.strict_host_key_checking == 2) {
1176			strlcat(msg, "\nAre you sure you want "
1177			    "to continue connecting (yes/no)? ", sizeof(msg));
1178			if (!confirm(msg))
1179				goto fail;
1180		} else {
1181			logit("%s", msg);
1182		}
1183	}
1184
1185	free(ip);
1186	free(host);
1187	if (host_hostkeys != NULL)
1188		free_hostkeys(host_hostkeys);
1189	if (ip_hostkeys != NULL)
1190		free_hostkeys(ip_hostkeys);
1191	return 0;
1192
1193fail:
1194	if (want_cert && host_status != HOST_REVOKED) {
1195		/*
1196		 * No matching certificate. Downgrade cert to raw key and
1197		 * search normally.
1198		 */
1199		debug("No matching CA found. Retry with plain key");
1200		raw_key = key_from_private(host_key);
1201		if (key_drop_cert(raw_key) != 0)
1202			fatal("Couldn't drop certificate");
1203		host_key = raw_key;
1204		goto retry;
1205	}
1206	if (raw_key != NULL)
1207		key_free(raw_key);
1208	free(ip);
1209	free(host);
1210	if (host_hostkeys != NULL)
1211		free_hostkeys(host_hostkeys);
1212	if (ip_hostkeys != NULL)
1213		free_hostkeys(ip_hostkeys);
1214	return -1;
1215}
1216
1217/* returns 0 if key verifies or -1 if key does NOT verify */
1218int
1219verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
1220{
1221	int flags = 0;
1222	char *fp;
1223	Key *plain = NULL;
1224
1225	fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
1226	debug("Server host key: %s %s", key_type(host_key), fp);
1227	free(fp);
1228
1229	if (options.verify_host_key_dns) {
1230		/*
1231		 * XXX certs are not yet supported for DNS, so downgrade
1232		 * them and try the plain key.
1233		 */
1234		plain = key_from_private(host_key);
1235		if (key_is_cert(plain))
1236			key_drop_cert(plain);
1237		if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) {
1238			if (flags & DNS_VERIFY_FOUND) {
1239				if (options.verify_host_key_dns == 1 &&
1240				    flags & DNS_VERIFY_MATCH &&
1241				    flags & DNS_VERIFY_SECURE) {
1242					key_free(plain);
1243					return 0;
1244				}
1245				if (flags & DNS_VERIFY_MATCH) {
1246					matching_host_key_dns = 1;
1247				} else {
1248					warn_changed_key(plain);
1249					error("Update the SSHFP RR in DNS "
1250					    "with the new host key to get rid "
1251					    "of this message.");
1252				}
1253			}
1254		}
1255		key_free(plain);
1256	}
1257
1258	return check_host_key(host, hostaddr, options.port, host_key, RDRW,
1259	    options.user_hostfiles, options.num_user_hostfiles,
1260	    options.system_hostfiles, options.num_system_hostfiles);
1261}
1262
1263/*
1264 * Starts a dialog with the server, and authenticates the current user on the
1265 * server.  This does not need any extra privileges.  The basic connection
1266 * to the server must already have been established before this is called.
1267 * If login fails, this function prints an error and never returns.
1268 * This function does not require super-user privileges.
1269 */
1270void
1271ssh_login(Sensitive *sensitive, const char *orighost,
1272    struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
1273{
1274	char *host;
1275	char *server_user, *local_user;
1276
1277	local_user = xstrdup(pw->pw_name);
1278	server_user = options.user ? options.user : local_user;
1279
1280	/* Convert the user-supplied hostname into all lowercase. */
1281	host = xstrdup(orighost);
1282	lowercase(host);
1283
1284	/* Exchange protocol version identification strings with the server. */
1285	ssh_exchange_identification(timeout_ms);
1286
1287	/* Put the connection into non-blocking mode. */
1288	packet_set_nonblocking();
1289
1290	/* key exchange */
1291	/* authenticate user */
1292	if (compat20) {
1293		ssh_kex2(host, hostaddr, port);
1294		ssh_userauth2(local_user, server_user, host, sensitive);
1295	} else {
1296		ssh_kex(host, hostaddr);
1297		ssh_userauth1(local_user, server_user, host, sensitive);
1298	}
1299	free(local_user);
1300}
1301
1302void
1303ssh_put_password(char *password)
1304{
1305	int size;
1306	char *padded;
1307
1308	if (datafellows & SSH_BUG_PASSWORDPAD) {
1309		packet_put_cstring(password);
1310		return;
1311	}
1312	size = roundup(strlen(password) + 1, 32);
1313	padded = xcalloc(1, size);
1314	strlcpy(padded, password, size);
1315	packet_put_string(padded, size);
1316	explicit_bzero(padded, size);
1317	free(padded);
1318}
1319
1320/* print all known host keys for a given host, but skip keys of given type */
1321static int
1322show_other_keys(struct hostkeys *hostkeys, Key *key)
1323{
1324	int type[] = {
1325		KEY_RSA1,
1326		KEY_RSA,
1327		KEY_DSA,
1328		KEY_ECDSA,
1329		KEY_ED25519,
1330		-1
1331	};
1332	int i, ret = 0;
1333	char *fp, *ra;
1334	const struct hostkey_entry *found;
1335
1336	for (i = 0; type[i] != -1; i++) {
1337		if (type[i] == key->type)
1338			continue;
1339		if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found))
1340			continue;
1341		fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX);
1342		ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART);
1343		logit("WARNING: %s key found for host %s\n"
1344		    "in %s:%lu\n"
1345		    "%s key fingerprint %s.",
1346		    key_type(found->key),
1347		    found->host, found->file, found->line,
1348		    key_type(found->key), fp);
1349		if (options.visual_host_key)
1350			logit("%s", ra);
1351		free(ra);
1352		free(fp);
1353		ret = 1;
1354	}
1355	return ret;
1356}
1357
1358static void
1359warn_changed_key(Key *host_key)
1360{
1361	char *fp;
1362
1363	fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
1364
1365	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1366	error("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @");
1367	error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
1368	error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
1369	error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
1370	error("It is also possible that a host key has just been changed.");
1371	error("The fingerprint for the %s key sent by the remote host is\n%s.",
1372	    key_type(host_key), fp);
1373	error("Please contact your system administrator.");
1374
1375	free(fp);
1376}
1377
1378/*
1379 * Execute a local command
1380 */
1381int
1382ssh_local_cmd(const char *args)
1383{
1384	char *shell;
1385	pid_t pid;
1386	int status;
1387	void (*osighand)(int);
1388
1389	if (!options.permit_local_command ||
1390	    args == NULL || !*args)
1391		return (1);
1392
1393	if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
1394		shell = _PATH_BSHELL;
1395
1396	osighand = signal(SIGCHLD, SIG_DFL);
1397	pid = fork();
1398	if (pid == 0) {
1399		signal(SIGPIPE, SIG_DFL);
1400		debug3("Executing %s -c \"%s\"", shell, args);
1401		execl(shell, shell, "-c", args, (char *)NULL);
1402		error("Couldn't execute %s -c \"%s\": %s",
1403		    shell, args, strerror(errno));
1404		_exit(1);
1405	} else if (pid == -1)
1406		fatal("fork failed: %.100s", strerror(errno));
1407	while (waitpid(pid, &status, 0) == -1)
1408		if (errno != EINTR)
1409			fatal("Couldn't wait for child: %s", strerror(errno));
1410	signal(SIGCHLD, osighand);
1411
1412	if (!WIFEXITED(status))
1413		return (1);
1414
1415	return (WEXITSTATUS(status));
1416}
1417