sshconnect.c revision 74197
1139826Simp/*
253541Sshin * Author: Tatu Ylonen <ylo@cs.hut.fi>
353541Sshin * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
453541Sshin *                    All rights reserved
553541Sshin * Code to connect to a remote host, and to perform the client side of the
653541Sshin * login (authentication) dialog.
753541Sshin *
853541Sshin * As far as I am concerned, the code I have written for this software
953541Sshin * can be used freely for any purpose.  Any derived versions of this
1053541Sshin * software must be clearly marked as such, and if the derived work is
1153541Sshin * incompatible with the protocol description in the RFC file, it must be
1253541Sshin * called by a name other than "ssh" or "Secure Shell".
1353541Sshin */
1453541Sshin
1553541Sshin#include "includes.h"
1653541SshinRCSID("$OpenBSD: sshconnect.c,v 1.79 2000/09/17 15:52:51 markus Exp $");
1753541SshinRCSID("$FreeBSD: head/crypto/openssh/sshconnect.c 74197 2001-03-13 04:42:38Z assar $");
1853541Sshin
1953541Sshin#include <openssl/bn.h>
2053541Sshin#include <openssl/dsa.h>
2153541Sshin#include <openssl/rsa.h>
2253541Sshin
2353541Sshin#include "xmalloc.h"
2453541Sshin#include "rsa.h"
2553541Sshin#include "ssh.h"
2653541Sshin#include "buffer.h"
2753541Sshin#include "packet.h"
2853541Sshin#include "uidswap.h"
2953541Sshin#include "compat.h"
30139826Simp#include "readconf.h"
3153541Sshin#include "key.h"
32180305Srwatson#include "sshconnect.h"
33180305Srwatson#include "hostfile.h"
3453541Sshin
3553541Sshinchar *client_version_string = NULL;
3653541Sshinchar *server_version_string = NULL;
3753541Sshin
3853541Sshinextern Options options;
3953541Sshinextern char *__progname;
4053541Sshin
4153541Sshin/*
4253541Sshin * Connect to the given ssh server using a proxy command.
4353541Sshin */
4453541Sshinint
4553541Sshinssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid,
4653541Sshin		  const char *proxy_command)
4753541Sshin{
4853541Sshin	Buffer command;
4953541Sshin	const char *cp;
5053541Sshin	char *command_string;
5153541Sshin	int pin[2], pout[2];
5253541Sshin	pid_t pid;
5353541Sshin	char strport[NI_MAXSERV];
5453541Sshin
5553541Sshin	/* Convert the port number into a string. */
5653541Sshin	snprintf(strport, sizeof strport, "%hu", port);
5753541Sshin
5853541Sshin	/* Build the final command string in the buffer by making the
5953541Sshin	   appropriate substitutions to the given proxy command. */
6053541Sshin	buffer_init(&command);
6153541Sshin	for (cp = proxy_command; *cp; cp++) {
62174510Sobrien		if (cp[0] == '%' && cp[1] == '%') {
63174510Sobrien			buffer_append(&command, "%", 1);
64174510Sobrien			cp++;
6555009Sshin			continue;
6678064Sume		}
6755009Sshin		if (cp[0] == '%' && cp[1] == 'h') {
6853541Sshin			buffer_append(&command, host, strlen(host));
6995759Stanimura			cp++;
70185435Sbz			continue;
71253085Sae		}
7295759Stanimura		if (cp[0] == '%' && cp[1] == 'p') {
7353541Sshin			buffer_append(&command, strport, strlen(strport));
7495759Stanimura			cp++;
75170689Srwatson			continue;
7653541Sshin		}
7795759Stanimura		buffer_append(&command, cp, 1);
7895759Stanimura	}
7953541Sshin	buffer_append(&command, "\0", 1);
8053541Sshin
8195759Stanimura	/* Get the final command string. */
82148385Sume	command_string = buffer_ptr(&command);
8353541Sshin
8453541Sshin	/* Create pipes for communicating with the proxy. */
8595759Stanimura	if (pipe(pin) < 0 || pipe(pout) < 0)
8653541Sshin		fatal("Could not create pipes to communicate with the proxy: %.100s",
87185571Sbz		      strerror(errno));
8853541Sshin
8953541Sshin	debug("Executing proxy command: %.500s", command_string);
9053541Sshin
9153541Sshin	/* Fork and execute the proxy command. */
92185571Sbz	if ((pid = fork()) == 0) {
93185571Sbz		char *argv[10];
9495759Stanimura
9562587Sitojun		/* Child.  Permanently give up superuser privileges. */
96211501Sanchie		permanently_set_uid(original_real_uid);
9795759Stanimura
9856723Sshin		/* Redirect stdin and stdout. */
9953541Sshin		close(pin[1]);
10095759Stanimura		if (pin[0] != 0) {
10153541Sshin			if (dup2(pin[0], 0) < 0)
10295759Stanimura				perror("dup2 stdin");
10362587Sitojun			close(pin[0]);
104211501Sanchie		}
10553541Sshin		close(pout[0]);
106171167Sgnn		if (dup2(pout[1], 1) < 0)
107105199Ssam			perror("dup2 stdout");
108105199Ssam		/* Cannot be 1 because pin allocated two descriptors. */
109171167Sgnn		close(pout[1]);
110105199Ssam
11153541Sshin		/* Stderr is left as it is so that error messages get
11253541Sshin		   printed on the user's terminal. */
11353541Sshin		argv[0] = "/bin/sh";
11453541Sshin		argv[1] = "-c";
11553541Sshin		argv[2] = command_string;
11653541Sshin		argv[3] = NULL;
11753541Sshin
11853541Sshin		/* Execute the proxy command.  Note that we gave up any
11953541Sshin		   extra privileges above. */
120195699Srwatson		execv("/bin/sh", argv);
121195699Srwatson		perror("/bin/sh");
122195727Srwatson		exit(1);
123195727Srwatson	}
124185348Szec	/* Parent. */
12553541Sshin	if (pid < 0)
12653541Sshin		fatal("fork failed: %.100s", strerror(errno));
12753541Sshin
128253085Sae	/* Close child side of the descriptors. */
129253085Sae	close(pin[0]);
130207369Sbz	close(pout[1]);
131253085Sae
132253085Sae	/* Free the command name. */
133253085Sae	buffer_free(&command);
134253085Sae
13553541Sshin	/* Set the connection file descriptors. */
136191672Sbms	packet_set_connection(pout[0], pin[1]);
137191672Sbms
138166938Sbms	return 1;
139191672Sbms}
140191672Sbms
141191672Sbms/*
142191672Sbms * Creates a (possibly privileged) socket for use as the ssh connection.
143195699Srwatson */
144191672Sbmsint
145191672Sbmsssh_create_socket(uid_t original_real_uid, int privileged, int family)
146191672Sbms{
147191672Sbms	int sock;
148166938Sbms
149166938Sbms	/*
150166938Sbms	 * If we are running as root and want to connect to a privileged
151166938Sbms	 * port, bind our own socket to a privileged port.
152194581Srdivacky	 */
153166938Sbms	if (privileged) {
154166938Sbms		int p = IPPORT_RESERVED - 1;
155180305Srwatson		sock = rresvport_af(&p, family);
156180305Srwatson		if (sock < 0)
15753541Sshin			error("rresvport: af=%d %.100s", family, strerror(errno));
15853541Sshin		else
159171259Sdelphij			debug("Allocated local port %d.", p);
16053541Sshin	} else {
161191672Sbms		/*
16253541Sshin		 * Just create an ordinary socket on arbitrary port.  We use
16353541Sshin		 * the user's uid to create the socket.
16453541Sshin		 */
16553541Sshin		temporarily_use_uid(original_real_uid);
16678064Sume		sock = socket(family, SOCK_STREAM, 0);
167121901Sume		if (sock < 0)
16853541Sshin			error("socket: %.100s", strerror(errno));
169252007Sae		restore_uid();
17078064Sume	}
17183934Sbrooks	return sock;
172180305Srwatson}
17378064Sume
174180305Srwatson/*
17553541Sshin * Opens a TCP/IP connection to the remote server on the given host.
17678064Sume * The canonical host name used to connect will be returned in *host.
177121901Sume * The address of the remote host will be returned in hostaddr.
17853541Sshin * If port is 0, the default port will be used.  If anonymous is zero,
179191672Sbms * a privileged port will be allocated to make the connection.
180191672Sbms * This requires super-user privileges if anonymous is false.
181181803Sbz * Connection_attempts specifies the maximum number of tries (one per
182181803Sbz * second).  If proxy_command is non-NULL, it specifies the command (with %h
183185435Sbz * and %p substituted for host and port, respectively) to use to contact
184186141Sbz * the daemon.
18553541Sshin */
186186141Sbzint
187186141Sbzssh_connect(char **host, struct sockaddr_storage * hostaddr,
188180850Smav	    u_short port, int connection_attempts,
18953541Sshin	    int anonymous, uid_t original_real_uid,
19053541Sshin	    const char *proxy_command)
191180850Smav{
19253541Sshin	int sock = -1, attempt;
19353541Sshin	struct servent *sp;
194180850Smav	struct addrinfo hints, *ai, *aitop;
195200473Sbz	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
196191672Sbms	int gaierr;
197191672Sbms	struct linger linger;
198191672Sbms
199191672Sbms	debug("ssh_connect: getuid %u geteuid %u anon %d",
200191672Sbms	      (u_int) getuid(), (u_int) geteuid(), anonymous);
201191672Sbms
202191672Sbms	/* Get default port if port has not been set. */
203191672Sbms	if (port == 0) {
204191672Sbms		sp = getservbyname(SSH_SERVICE_NAME, "tcp");
205191672Sbms		if (sp)
206248180Sae			port = ntohs(sp->s_port);
20778064Sume		else
208252007Sae			port = SSH_DEFAULT_PORT;
209151459Ssuz	}
21078064Sume	/* If a proxy command is given, connect using it. */
211180932Smav	if (proxy_command != NULL)
212252007Sae		return ssh_proxy_connect(*host, port, original_real_uid, proxy_command);
213180850Smav
21478064Sume	/* No proxy command. */
21553541Sshin
216191672Sbms	memset(&hints, 0, sizeof(hints));
217191672Sbms	hints.ai_family = IPv4or6;
218191672Sbms	hints.ai_socktype = SOCK_STREAM;
219191672Sbms	hints.ai_flags = AI_CANONNAME;
220191672Sbms	snprintf(strport, sizeof strport, "%d", port);
221191672Sbms	if ((gaierr = getaddrinfo(*host, strport, &hints, &aitop)) != 0)
222191672Sbms		fatal("%s: %.100s: %s", __progname, *host,
223191672Sbms		    gai_strerror(gaierr));
224199518Sbms
225199518Sbms	/*
226199518Sbms	 * Try to connect several times.  On some machines, the first time
227199518Sbms	 * will sometimes fail.  In general socket code appears to behave
228199518Sbms	 * quite magically on many machines.
229199518Sbms	 */
230199518Sbms	for (attempt = 0; attempt < connection_attempts; attempt++) {
231199518Sbms		if (attempt > 0)
232199518Sbms			debug("Trying again...");
233199518Sbms
234199518Sbms		/* Loop through addresses for this host, and try each one in
235199518Sbms		   sequence until the connection succeeds. */
236199518Sbms		for (ai = aitop; ai; ai = ai->ai_next) {
237199518Sbms			if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
238199518Sbms				continue;
239199518Sbms			if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
240199518Sbms			    ntop, sizeof(ntop), strport, sizeof(strport),
241191672Sbms			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
242191672Sbms				error("ssh_connect: getnameinfo failed");
243199518Sbms				continue;
244199518Sbms			}
245199518Sbms			debug("Connecting to %.200s [%.100s] port %s.",
246191672Sbms				ai->ai_canonname, ntop, strport);
247199518Sbms
248199518Sbms			/* Create a socket for connecting. */
249199518Sbms			sock = ssh_create_socket(original_real_uid,
250199518Sbms			    !anonymous && geteuid() == 0 && port < IPPORT_RESERVED,
251199518Sbms			    ai->ai_family);
252199518Sbms			if (sock < 0)
253199518Sbms				continue;
254199518Sbms
255199518Sbms			/* Connect to the host.  We use the user's uid in the
256199518Sbms			 * hope that it will help with tcp_wrappers showing
257191672Sbms			 * the remote uid as root.
258191672Sbms			 */
259211301Sbz			temporarily_use_uid(original_real_uid);
260191672Sbms			if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) {
261191672Sbms				/* Successful connection. */
262191672Sbms				memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
263186163Skmacy				restore_uid();
26453541Sshin				break;
26578064Sume			} else {
266171167Sgnn				debug("connect: %.100s", strerror(errno));
26778064Sume				restore_uid();
26878064Sume				/*
26978064Sume				 * Close the failed socket; there appear to
270125396Sume				 * be some problems when reusing a socket for
27178064Sume				 * which connect() has already returned an
272253571Sae				 * error.
273180305Srwatson				 */
274105199Ssam				shutdown(sock, SHUT_RDWR);
275171167Sgnn				close(sock);
27653541Sshin			}
277186223Sbz		}
278186141Sbz		if (ai) {
279121674Sume#if 0
28053541Sshin			if (ai->ai_canonname != NULL)
28153541Sshin				*host = xstrdup(ai->ai_canonname);
282186141Sbz#endif
283121901Sume			break;	/* Successful connection. */
28453541Sshin		}
28553541Sshin
28653541Sshin		/* Sleep a moment before retrying. */
28753541Sshin		sleep(1);
288252007Sae	}
28997658Stanimura
290186141Sbz	freeaddrinfo(aitop);
29153541Sshin
29253541Sshin	/* Return failure if we didn't get a successful connection. */
293178377Srwatson	if (attempt >= connection_attempts)
29453541Sshin		return 0;
29553541Sshin
29653541Sshin	debug("Connection established.");
297181803Sbz
298171167Sgnn	/*
29978064Sume	 * Set socket options.  We would like the socket to disappear as soon
30078064Sume	 * as it has been closed for whatever reason.
30178064Sume	 */
302186170Skmacy	/* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */
30378064Sume	linger.l_onoff = 1;
304253571Sae	linger.l_linger = 5;
305249294Sae	setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger));
306180305Srwatson
307178377Srwatson	/* Set the connection. */
308105199Ssam	packet_set_connection(sock, sock);
309171167Sgnn
310186163Skmacy	return 1;
311186223Sbz}
312186141Sbz
313121674Sume/*
314180305Srwatson * Waits for the server identification string, and sends our own
31553541Sshin * identification string.
316186141Sbz */
317180305Srwatsonvoid
31853541Sshinssh_exchange_identification()
31953541Sshin{
32053541Sshin	char buf[256], remote_version[256];	/* must be same size! */
321252007Sae	int remote_major, remote_minor, i, mismatch;
32297658Stanimura	int connection_in = packet_get_connection_in();
323186141Sbz	int connection_out = packet_get_connection_out();
324178377Srwatson
32553541Sshin	/* Read other side\'s version identification. */
326252007Sae	for (;;) {
32778064Sume		for (i = 0; i < sizeof(buf) - 1; i++) {
328252007Sae			int len = atomicio(read, connection_in, &buf[i], 1);
32953541Sshin			if (len < 0)
33053541Sshin				fatal("ssh_exchange_identification: read: %.100s", strerror(errno));
33153541Sshin			if (len != 1)
33253541Sshin				fatal("ssh_exchange_identification: Connection closed by remote host");
33353541Sshin			if (buf[i] == '\r') {
334180305Srwatson				buf[i] = '\n';
335180305Srwatson				buf[i + 1] = 0;
33653541Sshin				continue;		/**XXX wait for \n */
337249294Sae			}
33853541Sshin			if (buf[i] == '\n') {
339180305Srwatson				buf[i + 1] = 0;
34053541Sshin				break;
34153541Sshin			}
34262587Sitojun		}
343171259Sdelphij		buf[sizeof(buf) - 1] = 0;
34462587Sitojun		if (strncmp(buf, "SSH-", 4) == 0)
34562587Sitojun			break;
34662587Sitojun		debug("ssh_exchange_identification: %s", buf);
34762587Sitojun	}
34878064Sume	server_version_string = xstrdup(buf);
34978064Sume
350125776Sume	/*
351175162Sobrien	 * Check that the versions match.  In future this might accept
35262587Sitojun	 * several versions and set appropriate flags to handle them.
35362587Sitojun	 */
35462587Sitojun	if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n",
35562587Sitojun	    &remote_major, &remote_minor, remote_version) != 3)
35662587Sitojun		fatal("Bad remote protocol version identification: '%.100s'", buf);
35762587Sitojun	debug("Remote protocol version %d.%d, remote software version %.100s",
35862587Sitojun	      remote_major, remote_minor, remote_version);
35962587Sitojun
36062587Sitojun	compat_datafellows(remote_version);
36162587Sitojun	mismatch = 0;
36262587Sitojun
36362587Sitojun	switch(remote_major) {
36462587Sitojun	case 1:
36562587Sitojun		if (remote_minor == 99 &&
366180305Srwatson		    (options.protocol & SSH_PROTO_2) &&
367180305Srwatson		    !(options.protocol & SSH_PROTO_1_PREFERRED)) {
368180305Srwatson			enable_compat20();
36962587Sitojun			break;
37078064Sume		}
37162587Sitojun		if (!(options.protocol & SSH_PROTO_1)) {
37262587Sitojun			mismatch = 1;
37362587Sitojun			break;
374125776Sume		}
37578064Sume		if (remote_minor < 3) {
37662587Sitojun			fatal("Remote machine has too old SSH software version.");
37762587Sitojun		} else if (remote_minor == 3) {
37862587Sitojun			/* We speak 1.3, too. */
379125776Sume			enable_compat13();
38078064Sume			if (options.forward_agent) {
38162587Sitojun				log("Agent forwarding disabled for protocol 1.3");
38262587Sitojun				options.forward_agent = 0;
383181803Sbz			}
384180305Srwatson		}
38562587Sitojun		break;
38662587Sitojun	case 2:
38753541Sshin		if (options.protocol & SSH_PROTO_2) {
388180305Srwatson			enable_compat20();
389180305Srwatson			break;
39053541Sshin		}
39153541Sshin		/* FALLTHROUGH */
39253541Sshin	default:
39353541Sshin		mismatch = 1;
39453541Sshin		break;
39553541Sshin	}
39653541Sshin	if (mismatch)
39753541Sshin		fatal("Protocol major versions differ: %d vs. %d",
39853541Sshin		    (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
39953541Sshin		    remote_major);
400120941Sume	if (compat20)
401211501Sanchie		packet_set_ssh2_format();
40253541Sshin	/* Send our own protocol version identification. */
40353541Sshin	snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
40453541Sshin	    compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
40553541Sshin	    compat20 ? PROTOCOL_MINOR_2 : PROTOCOL_MINOR_1,
40653541Sshin	    SSH_VERSION);
40753541Sshin	if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf))
40853541Sshin		fatal("write: %.100s", strerror(errno));
409148247Sume	client_version_string = xstrdup(buf);
41053541Sshin	chop(client_version_string);
41153541Sshin	chop(server_version_string);
412148385Sume	debug("Local version string %.100s", client_version_string);
413211435Sume}
414194777Sbz
41553541Sshinint
41653541Sshinread_yes_or_no(const char *prompt, int defval)
41753541Sshin{
41853541Sshin	char buf[1024];
41953541Sshin	FILE *f;
42053541Sshin	int retval = -1;
42153541Sshin
42253541Sshin	if (isatty(0))
423186141Sbz		f = stdin;
424178285Srwatson	else
42553541Sshin		f = fopen("/dev/tty", "rw");
42653541Sshin
427186170Skmacy	if (f == NULL)
428148242Sume		return 0;
429175630Sbz
430175630Sbz	fflush(stdout);
43153541Sshin
432121472Sume	while (1) {
433148247Sume		fprintf(stderr, "%s", prompt);
434148247Sume		if (fgets(buf, sizeof(buf), f) == NULL) {
435148247Sume			/* Print a newline (the prompt probably didn\'t have one). */
43653541Sshin			fprintf(stderr, "\n");
43753541Sshin			strlcpy(buf, "no", sizeof buf);
438148385Sume		}
439180305Srwatson		/* Remove newline from response. */
440148385Sume		if (strchr(buf, '\n'))
441148385Sume			*strchr(buf, '\n') = 0;
442148385Sume
443211530Sume		if (buf[0] == 0)
444211530Sume			retval = defval;
445211435Sume		if (strcmp(buf, "yes") == 0)
446211435Sume			retval = 1;
447148385Sume		else if (strcmp(buf, "no") == 0)
448211435Sume			retval = 0;
449148385Sume		else
450148385Sume			fprintf(stderr, "Please type 'yes' or 'no'.\n");
451148385Sume
452148385Sume		if (retval != -1) {
453180305Srwatson			if (f != stdin)
454180305Srwatson				fclose(f);
45553541Sshin			return retval;
45653541Sshin		}
45753541Sshin	}
45853541Sshin}
45953541Sshin
46053541Sshin/*
46153541Sshin * check whether the supplied host key is valid, return only if ok.
46253541Sshin */
46353541Sshin
46453541Sshinvoid
46553541Sshincheck_host_key(char *host, struct sockaddr *hostaddr, Key *host_key,
46653541Sshin	const char *user_hostfile, const char *system_hostfile)
46753541Sshin{
468243882Sglebius	Key *file_key;
469133592Srwatson	char *type = key_type(host_key);
470133592Srwatson	char *ip = NULL;
471133592Srwatson	char hostline[1000], *hostp;
472133592Srwatson	HostStatus host_status;
47353541Sshin	HostStatus ip_status;
47453541Sshin	int local = 0, host_ip_differ = 0;
47553541Sshin	char ntop[NI_MAXHOST];
47653541Sshin
47753541Sshin	/*
478194777Sbz	 * Force accepting of the host key for loopback/localhost. The
479194777Sbz	 * problem is that if the home directory is NFS-mounted to multiple
480194777Sbz	 * machines, localhost will refer to a different machine in each of
481121472Sume	 * them, and the user will get bogus HOST_CHANGED warnings.  This
482207277Sbz	 * essentially disables host authentication for localhost; however,
483188144Sjamie	 * this is probably not a real problem.
484188144Sjamie	 */
485194777Sbz	/**  hostaddr == 0! */
486148385Sume	switch (hostaddr->sa_family) {
487148385Sume	case AF_INET:
488148385Sume		local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
489148385Sume		break;
490148385Sume	case AF_INET6:
491148385Sume		local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
492148385Sume		break;
493148385Sume	default:
494148385Sume		local = 0;
495148385Sume		break;
496148385Sume	}
497148385Sume	if (local) {
498148385Sume		debug("Forcing accepting of host key for loopback/localhost.");
499148385Sume		return;
500148385Sume	}
501148385Sume
502180305Srwatson	/*
503180305Srwatson	 * Turn off check_host_ip for proxy connects, since
504180305Srwatson	 * we don't have the remote ip-address
50555009Sshin	 */
506186141Sbz	if (options.proxy_command != NULL && options.check_host_ip)
50755009Sshin		options.check_host_ip = 0;
508180305Srwatson
509180305Srwatson	if (options.check_host_ip) {
510180305Srwatson		if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop),
511180305Srwatson		    NULL, 0, NI_NUMERICHOST) != 0)
512180305Srwatson			fatal("check_host_key: getnameinfo failed");
513186141Sbz		ip = xstrdup(ntop);
51453541Sshin	}
51553541Sshin
51653541Sshin	/*
51753541Sshin	 * Store the host key from the known host file in here so that we can
51853541Sshin	 * compare it with the key for the IP address.
51953541Sshin	 */
52053541Sshin	file_key = key_new(host_key->type);
52153541Sshin
522180305Srwatson	/*
52353541Sshin	 * Check if the host key is present in the user\'s list of known
52453541Sshin	 * hosts or in the systemwide list.
52553541Sshin	 */
52653541Sshin	host_status = check_host_in_hostfile(user_hostfile, host, host_key, file_key);
52753541Sshin	if (host_status == HOST_NEW)
52853541Sshin		host_status = check_host_in_hostfile(system_hostfile, host, host_key, file_key);
52953541Sshin	/*
53053541Sshin	 * Also perform check for the ip address, skip the check if we are
53153541Sshin	 * localhost or the hostname was an ip address to begin with
53253541Sshin	 */
53353541Sshin	if (options.check_host_ip && !local && strcmp(host, ip)) {
53453541Sshin		Key *ip_key = key_new(host_key->type);
53553541Sshin		ip_status = check_host_in_hostfile(user_hostfile, ip, host_key, ip_key);
53653541Sshin
53753541Sshin		if (ip_status == HOST_NEW)
53853541Sshin			ip_status = check_host_in_hostfile(system_hostfile, ip, host_key, ip_key);
53953541Sshin		if (host_status == HOST_CHANGED &&
54053541Sshin		    (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key)))
54153541Sshin			host_ip_differ = 1;
54253541Sshin
54353541Sshin		key_free(ip_key);
54453541Sshin	} else
545211501Sanchie		ip_status = host_status;
546211501Sanchie
547211501Sanchie	key_free(file_key);
548211501Sanchie
549211501Sanchie	switch (host_status) {
550211501Sanchie	case HOST_OK:
551211501Sanchie		/* The host is known and the key matches. */
552211501Sanchie		debug("Host '%.200s' is known and matches the %s host key.",
553211501Sanchie		    host, type);
554211501Sanchie		if (options.check_host_ip) {
555211501Sanchie			if (ip_status == HOST_NEW) {
556211501Sanchie				if (!add_host_to_hostfile(user_hostfile, ip, host_key))
557211501Sanchie					log("Failed to add the %s host key for IP address '%.30s' to the list of known hosts (%.30s).",
558211501Sanchie					    type, ip, user_hostfile);
559211501Sanchie				else
560211501Sanchie					log("Warning: Permanently added the %s host key for IP address '%.30s' to the list of known hosts.",
561211501Sanchie					    type, ip);
562148247Sume			} else if (ip_status != HOST_OK)
56353541Sshin				log("Warning: the %s host key for '%.200s' differs from the key for the IP address '%.30s'",
56453541Sshin				    type, host, ip);
56553541Sshin		}
566190964Srwatson		break;
56778064Sume	case HOST_NEW:
568252007Sae		/* The host is new. */
56953541Sshin		if (options.strict_host_key_checking == 1) {
57053541Sshin			/* User has requested strict host key checking.  We will not add the host key
57153541Sshin			   automatically.  The only alternative left is to abort. */
57253541Sshin			fatal("No %s host key is known for %.200s and you have requested strict checking.", type, host);
57353541Sshin		} else if (options.strict_host_key_checking == 2) {
57453541Sshin			/* The default */
57553541Sshin			char prompt[1024];
57653541Sshin			char *fp = key_fingerprint(host_key);
577186170Skmacy			snprintf(prompt, sizeof(prompt),
578148247Sume			    "The authenticity of host '%.200s' can't be established.\n"
57953541Sshin			    "%s key fingerprint is %s.\n"
58078064Sume			    "Are you sure you want to continue connecting (yes/no)? ",
581178285Srwatson			    host, type, fp);
582120856Sume			if (!read_yes_or_no(prompt, -1))
58353541Sshin				fatal("Aborted by user!\n");
58453541Sshin		}
58553541Sshin		if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) {
58653541Sshin			snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
58753541Sshin			hostp = hostline;
58853541Sshin		} else
589171259Sdelphij			hostp = host;
59053541Sshin
591231852Sbz		/* If not in strict mode, add the key automatically to the local known_hosts file. */
59253541Sshin		if (!add_host_to_hostfile(user_hostfile, hostp, host_key))
59353541Sshin			log("Failed to add the host to the list of known hosts (%.500s).",
59453541Sshin			    user_hostfile);
59553541Sshin		else
59653541Sshin			log("Warning: Permanently added '%.200s' (%s) to the list of known hosts.",
59753541Sshin			    hostp, type);
59853541Sshin		break;
599120856Sume	case HOST_CHANGED:
600231852Sbz		if (options.check_host_ip && host_ip_differ) {
601231852Sbz			char *msg;
602231852Sbz			if (ip_status == HOST_NEW)
603231852Sbz				msg = "is unknown";
604231852Sbz			else if (ip_status == HOST_OK)
605231852Sbz				msg = "is unchanged";
606231852Sbz			else
607231852Sbz				msg = "has a different value";
608231852Sbz			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
60953541Sshin			error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
610231852Sbz			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
61153541Sshin			error("The %s host key for %s has changed,", type, host);
61253541Sshin			error("and the key for the according IP address %s", ip);
61353541Sshin			error("%s. This could either mean that", msg);
61453541Sshin			error("DNS SPOOFING is happening or the IP address for the host");
61553541Sshin			error("and its host key have changed at the same time");
61653541Sshin		}
61756723Sshin		/* The host key has changed. */
61856723Sshin		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
61956723Sshin		error("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @");
62056723Sshin		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
62156723Sshin		error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
62256723Sshin		error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
62356723Sshin		error("It is also possible that the %s host key has just been changed.", type);
624166938Sbms		error("Please contact your system administrator.");
625166938Sbms		error("Add correct host key in %.100s to get rid of this message.",
62656723Sshin		      user_hostfile);
627121578Sume
628121578Sume		/*
629121578Sume		 * If strict host key checking is in use, the user will have
63053541Sshin		 * to edit the key manually and we can only abort.
63153541Sshin		 */
63253541Sshin		if (options.strict_host_key_checking)
63353541Sshin			fatal("%s host key for %.200s has changed and you have requested strict checking.", type, host);
63453541Sshin
63553541Sshin		/*
63653541Sshin		 * If strict host key checking has not been requested, allow
63753541Sshin		 * the connection but without password authentication or
63856723Sshin		 * agent forwarding.
63956723Sshin		 */
64056723Sshin		if (options.password_authentication) {
64156723Sshin			error("Password authentication is disabled to avoid trojan horses.");
64256723Sshin			options.password_authentication = 0;
64356723Sshin		}
64456723Sshin		if (options.forward_agent) {
645166938Sbms			error("Agent forwarding is disabled to avoid trojan horses.");
646166938Sbms			options.forward_agent = 0;
64756723Sshin		}
648121578Sume		/*
649121578Sume		 * XXX Should permit the user to change to use the new id.
650121578Sume		 * This could be done by converting the host key to an
65153541Sshin		 * identifying sentence, tell that the host identifies itself
65253541Sshin		 * by that sentence, and ask the user if he/she whishes to
65353541Sshin		 * accept the authentication.
65453541Sshin		 */
65553541Sshin		break;
65653541Sshin	}
65753541Sshin	if (options.check_host_ip)
65853541Sshin		xfree(ip);
65953541Sshin}
66053541Sshin
66153541Sshin#ifdef KRB5
66283366Sjulianint
66353541Sshintry_krb5_authentication(krb5_context *context, krb5_auth_context *auth_context)
66453541Sshin{
665144261Ssam  krb5_error_code problem;
666157676Srwatson  const char *tkfile;
66753541Sshin  struct stat buf;
66853541Sshin  krb5_ccache ccache = NULL;
669157374Srwatson  const char *remotehost;
670180305Srwatson  krb5_data ap;
671175630Sbz  int type, payload_len;
672175630Sbz  krb5_ap_rep_enc_part *reply = NULL;
673180305Srwatson  int ret;
67455009Sshin
675157374Srwatson  memset(&ap, 0, sizeof(ap));
676180305Srwatson
677184214Sdes  problem = krb5_init_context(context);
678157374Srwatson  if (problem) {
679180305Srwatson     ret = 0;
680181803Sbz     goto out;
681181803Sbz  }
682132714Srwatson
683181803Sbz  tkfile = krb5_cc_default_name(*context);
684184205Sdes  if (strncmp(tkfile, "FILE:", 5) == 0)
685180305Srwatson     tkfile += 5;
686132714Srwatson
68753541Sshin  if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) {
688181803Sbz    debug("Kerberos V5: could not get default ccache (permission denied).");
68953541Sshin    ret = 0;
690186141Sbz    goto out;
69153541Sshin  }
69253541Sshin
693144261Ssam  problem = krb5_cc_default(*context, &ccache);
69453541Sshin  if (problem) {
695178285Srwatson     ret = 0;
696180305Srwatson     goto out;
69753541Sshin  }
69853541Sshin
699157370Srwatson  remotehost = get_canonical_hostname();
70053541Sshin
70153541Sshin  problem = krb5_mk_req(*context, auth_context, AP_OPTS_MUTUAL_REQUIRED,
70253541Sshin			"host", remotehost, NULL, ccache, &ap);
70353541Sshin  if (problem) {
70453541Sshin     ret = 0;
705157374Srwatson     goto out;
706160549Srwatson  }
707191672Sbms
708166938Sbms  packet_start(SSH_CMSG_AUTH_KERBEROS);
70953541Sshin  packet_put_string((char *) ap.data, ap.length);
710181803Sbz  packet_send();
711178285Srwatson  packet_write_wait();
712184205Sdes
713185344Sbz  xfree(ap.data);
714185370Sbz  ap.length = 0;
715181803Sbz
71653541Sshin  type = packet_read(&payload_len);
71753541Sshin   switch (type) {
718160549Srwatson        case SSH_SMSG_FAILURE:
719157366Srwatson                /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */
72053541Sshin                debug("Kerberos V5 authentication failed.");
72153541Sshin                ret = 0;
722160549Srwatson                break;
723160549Srwatson
724160549Srwatson         case SSH_SMSG_AUTH_KERBEROS_RESPONSE:
725160549Srwatson                /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */
726160549Srwatson                debug("Kerberos V5 authentication accepted.");
72753541Sshin
72853541Sshin                /* Get server's response. */
72953541Sshin                ap.data = packet_get_string((unsigned int *) &ap.length);
730160549Srwatson
731160549Srwatson                packet_integrity_check(payload_len, 4 + ap.length, type);
732160549Srwatson                /* XXX je to dobre? */
733160549Srwatson
734160549Srwatson                problem = krb5_rd_rep(*context, *auth_context, &ap, &reply);
735160549Srwatson                if (problem) {
736160549Srwatson		   ret = 0;
737160549Srwatson                }
738160549Srwatson		ret = 1;
739160549Srwatson		break;
740160549Srwatson
74153541Sshin	default:
74253541Sshin		packet_disconnect("Protocol error on Kerberos V5 response: %d", type);
74353541Sshin		ret = 0;
744180305Srwatson		break;
74553541Sshin
746180305Srwatson   }
747180305Srwatson
748180305Srwatsonout:
74997658Stanimura   if (ccache != NULL)
750180305Srwatson       krb5_cc_close(*context, ccache);
75153541Sshin   if (reply != NULL)
752157366Srwatson      krb5_free_ap_rep_enc_part(*context, reply);
753157374Srwatson   if (ap.length > 0)
75453541Sshin      krb5_data_free(&ap);
75553541Sshin
75653541Sshin   return ret;
75783366Sjulian
75853541Sshin}
759180305Srwatson
76053541Sshinvoid
761194760Srwatsonsend_krb5_tgt(krb5_context context, krb5_auth_context auth_context)
762148385Sume{
76353541Sshin  int fd;
764180305Srwatson  int type, payload_len;
765157374Srwatson  krb5_error_code problem;
766180305Srwatson  krb5_data outbuf;
76753541Sshin  krb5_ccache ccache = NULL;
768180305Srwatson  krb5_creds creds;
769188144Sjamie  krb5_kdc_flags flags;
770188144Sjamie  const char* remotehost = get_canonical_hostname();
771181803Sbz
772180305Srwatson  memset(&creds, 0, sizeof(creds));
773181803Sbz  memset(&outbuf, 0, sizeof(outbuf));
774180305Srwatson
775148385Sume  fd = packet_get_connection_in();
77653541Sshin  problem = krb5_auth_con_setaddrs_from_fd(context, auth_context, &fd);
777194760Srwatson  if (problem) {
778180305Srwatson     goto out;
779194760Srwatson  }
780194760Srwatson
78153541Sshin#if 0
78253541Sshin  tkfile = krb5_cc_default_name(context);
783194760Srwatson  if (strncmp(tkfile, "FILE:", 5) == 0)
784120856Sume     tkfile += 5;
78553541Sshin
786194760Srwatson  if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) {
787194760Srwatson     debug("Kerberos V5: could not get default ccache (permission denied).");
788181803Sbz     goto out;
789178285Srwatson  }
79053541Sshin#endif
791178285Srwatson
792181803Sbz  problem = krb5_cc_default(context, &ccache);
793180305Srwatson  if (problem) {
79453541Sshin     goto out;
79553541Sshin  }
79653541Sshin
79783366Sjulian  problem = krb5_cc_get_principal(context, ccache, &creds.client);
79853541Sshin  if (problem) {
799180305Srwatson     goto out;
80053541Sshin  }
801194777Sbz
802148385Sume  problem = krb5_build_principal(context, &creds.server,
803148385Sume	                         strlen(creds.client->realm),
80453541Sshin				 creds.client->realm,
805180305Srwatson				 "krbtgt",
806157374Srwatson				 creds.client->realm,
807180305Srwatson				 NULL);
80853541Sshin  if (problem) {
809180305Srwatson     goto out;
810181803Sbz  }
811180305Srwatson
81253541Sshin  creds.times.endtime = 0;
813180305Srwatson
814148385Sume  flags.i = 0;
815148385Sume  flags.b.forwarded = 1;
816180305Srwatson  flags.b.forwardable = krb5_config_get_bool(context,  NULL,
817180305Srwatson	                  "libdefaults", "forwardable", NULL);
818180305Srwatson
819180305Srwatson  problem = krb5_get_forwarded_creds (context,
820180305Srwatson	                              auth_context,
821180305Srwatson				      ccache,
822148385Sume				      flags.i,
823181803Sbz				      remotehost,
824148385Sume				      &creds,
825181803Sbz				      &outbuf);
826180305Srwatson  if (problem) {
827148385Sume     goto out;
828181803Sbz  }
829178285Srwatson
83053541Sshin  packet_start(SSH_CMSG_HAVE_KERBEROS_TGT);
831194777Sbz  packet_put_string((char *)outbuf.data, outbuf.length);
832194777Sbz  packet_send();
833194777Sbz  packet_write_wait();
834178285Srwatson
835181803Sbz  type = packet_read(&payload_len);
836194777Sbz  switch (type) {
837132714Srwatson     case SSH_SMSG_SUCCESS:
838148385Sume	break;
839148385Sume     case SSH_SMSG_FAILURE:
840148385Sume	break;
841148385Sume     default:
842178285Srwatson	break;
843181803Sbz  }
844180305Srwatson
845148385Sumeout:
846148385Sume  if (creds.client)
847194777Sbz     krb5_free_principal(context, creds.client);
84853541Sshin  if (creds.server)
849178285Srwatson     krb5_free_principal(context, creds.server);
850181803Sbz  if (ccache)
851180305Srwatson     krb5_cc_close(context, ccache);
85253541Sshin  if (outbuf.data)
85353541Sshin     xfree(outbuf.data);
85453541Sshin
85553541Sshin  return;
85653541Sshin}
857132714Srwatson#endif /* KRB5 */
858132714Srwatson
859132714Srwatson/*
860157374Srwatson * Starts a dialog with the server, and authenticates the current user on the
861180305Srwatson * server.  This does not need any extra privileges.  The basic connection
862178285Srwatson * to the server must already have been established before this is called.
86353541Sshin * If login fails, this function prints an error and never returns.
864178285Srwatson * This function does not require super-user privileges.
865180305Srwatson */
86653541Sshinvoid
86753541Sshinssh_login(int host_key_valid, RSA *own_host_key, const char *orighost,
86853541Sshin    struct sockaddr *hostaddr, uid_t original_real_uid)
86953541Sshin{
870171260Sdelphij	struct passwd *pw;
87153541Sshin	char *host, *cp;
872180305Srwatson	char *server_user, *local_user;
87353541Sshin
87453541Sshin	/* Get local user name.  Use it as server user if no user name was given. */
875132714Srwatson	pw = getpwuid(original_real_uid);
87653541Sshin	if (!pw)
877180305Srwatson		fatal("User id %u not found from user database.", original_real_uid);
878157374Srwatson	local_user = xstrdup(pw->pw_name);
879180305Srwatson	server_user = options.user ? options.user : local_user;
880180305Srwatson
881132714Srwatson	/* Convert the user-supplied hostname into all lowercase. */
88253541Sshin	host = xstrdup(orighost);
88353541Sshin	for (cp = host; *cp; cp++)
88453541Sshin		if (isupper(*cp))
885180305Srwatson			*cp = tolower(*cp);
88653541Sshin
88753541Sshin	/* Exchange protocol version identification strings with the server. */
88853541Sshin	ssh_exchange_identification();
88953541Sshin
89053541Sshin	/* Put the connection into non-blocking mode. */
891180990Srwatson	packet_set_nonblocking();
89253541Sshin
893180990Srwatson	/* key exchange */
894180990Srwatson	/* authenticate user */
89553541Sshin	if (compat20) {
89653541Sshin		ssh_kex2(host, hostaddr);
89753541Sshin		ssh_userauth2(server_user, host);
89853541Sshin	} else {
899180305Srwatson		ssh_kex(host, hostaddr);
90053541Sshin		ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key);
901148385Sume	}
902148385Sume}
903180305Srwatson