xref: /freebsd/crypto/openssh/sshconnect.c (revision b601c69bdbe8755d26570261d7fd4c02ee4eff74)
1 /*
2  * Author: Tatu Ylonen <ylo@cs.hut.fi>
3  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4  *                    All rights reserved
5  * Created: Sat Mar 18 22:15:47 1995 ylo
6  * Code to connect to a remote host, and to perform the client side of the
7  * login (authentication) dialog.
8  *
9  * $FreeBSD$
10  */
11 
12 #include "includes.h"
13 RCSID("$OpenBSD: sshconnect.c,v 1.74 2000/05/17 16:57:02 markus Exp $");
14 
15 #include <openssl/bn.h>
16 #include <openssl/dsa.h>
17 #include <openssl/rsa.h>
18 
19 #include "xmalloc.h"
20 #include "rsa.h"
21 #include "ssh.h"
22 #include "buffer.h"
23 #include "packet.h"
24 #include "uidswap.h"
25 #include "compat.h"
26 #include "readconf.h"
27 #include "key.h"
28 #include "sshconnect.h"
29 #include "hostfile.h"
30 
31 char *client_version_string = NULL;
32 char *server_version_string = NULL;
33 
34 extern Options options;
35 extern char *__progname;
36 
37 /*
38  * Connect to the given ssh server using a proxy command.
39  */
40 int
41 ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid,
42 		  const char *proxy_command)
43 {
44 	Buffer command;
45 	const char *cp;
46 	char *command_string;
47 	int pin[2], pout[2];
48 	pid_t pid;
49 	char strport[NI_MAXSERV];
50 
51 	/* Convert the port number into a string. */
52 	snprintf(strport, sizeof strport, "%hu", port);
53 
54 	/* Build the final command string in the buffer by making the
55 	   appropriate substitutions to the given proxy command. */
56 	buffer_init(&command);
57 	for (cp = proxy_command; *cp; cp++) {
58 		if (cp[0] == '%' && cp[1] == '%') {
59 			buffer_append(&command, "%", 1);
60 			cp++;
61 			continue;
62 		}
63 		if (cp[0] == '%' && cp[1] == 'h') {
64 			buffer_append(&command, host, strlen(host));
65 			cp++;
66 			continue;
67 		}
68 		if (cp[0] == '%' && cp[1] == 'p') {
69 			buffer_append(&command, strport, strlen(strport));
70 			cp++;
71 			continue;
72 		}
73 		buffer_append(&command, cp, 1);
74 	}
75 	buffer_append(&command, "\0", 1);
76 
77 	/* Get the final command string. */
78 	command_string = buffer_ptr(&command);
79 
80 	/* Create pipes for communicating with the proxy. */
81 	if (pipe(pin) < 0 || pipe(pout) < 0)
82 		fatal("Could not create pipes to communicate with the proxy: %.100s",
83 		      strerror(errno));
84 
85 	debug("Executing proxy command: %.500s", command_string);
86 
87 	/* Fork and execute the proxy command. */
88 	if ((pid = fork()) == 0) {
89 		char *argv[10];
90 
91 		/* Child.  Permanently give up superuser privileges. */
92 		permanently_set_uid(original_real_uid);
93 
94 		/* Redirect stdin and stdout. */
95 		close(pin[1]);
96 		if (pin[0] != 0) {
97 			if (dup2(pin[0], 0) < 0)
98 				perror("dup2 stdin");
99 			close(pin[0]);
100 		}
101 		close(pout[0]);
102 		if (dup2(pout[1], 1) < 0)
103 			perror("dup2 stdout");
104 		/* Cannot be 1 because pin allocated two descriptors. */
105 		close(pout[1]);
106 
107 		/* Stderr is left as it is so that error messages get
108 		   printed on the user's terminal. */
109 		argv[0] = "/bin/sh";
110 		argv[1] = "-c";
111 		argv[2] = command_string;
112 		argv[3] = NULL;
113 
114 		/* Execute the proxy command.  Note that we gave up any
115 		   extra privileges above. */
116 		execv("/bin/sh", argv);
117 		perror("/bin/sh");
118 		exit(1);
119 	}
120 	/* Parent. */
121 	if (pid < 0)
122 		fatal("fork failed: %.100s", strerror(errno));
123 
124 	/* Close child side of the descriptors. */
125 	close(pin[0]);
126 	close(pout[1]);
127 
128 	/* Free the command name. */
129 	buffer_free(&command);
130 
131 	/* Set the connection file descriptors. */
132 	packet_set_connection(pout[0], pin[1]);
133 
134 	return 1;
135 }
136 
137 /*
138  * Creates a (possibly privileged) socket for use as the ssh connection.
139  */
140 int
141 ssh_create_socket(uid_t original_real_uid, int privileged, int family)
142 {
143 	int sock;
144 
145 	/*
146 	 * If we are running as root and want to connect to a privileged
147 	 * port, bind our own socket to a privileged port.
148 	 */
149 	if (privileged) {
150 		int p = IPPORT_RESERVED - 1;
151 		sock = rresvport_af(&p, family);
152 		if (sock < 0)
153 			error("rresvport: af=%d %.100s", family, strerror(errno));
154 		else
155 			debug("Allocated local port %d.", p);
156 	} else {
157 		/*
158 		 * Just create an ordinary socket on arbitrary port.  We use
159 		 * the user's uid to create the socket.
160 		 */
161 		temporarily_use_uid(original_real_uid);
162 		sock = socket(family, SOCK_STREAM, 0);
163 		if (sock < 0)
164 			error("socket: %.100s", strerror(errno));
165 		restore_uid();
166 	}
167 	return sock;
168 }
169 
170 /*
171  * Opens a TCP/IP connection to the remote server on the given host.
172  * The address of the remote host will be returned in hostaddr.
173  * If port is 0, the default port will be used.  If anonymous is zero,
174  * a privileged port will be allocated to make the connection.
175  * This requires super-user privileges if anonymous is false.
176  * Connection_attempts specifies the maximum number of tries (one per
177  * second).  If proxy_command is non-NULL, it specifies the command (with %h
178  * and %p substituted for host and port, respectively) to use to contact
179  * the daemon.
180  */
181 int
182 ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
183 	    u_short port, int connection_attempts,
184 	    int anonymous, uid_t original_real_uid,
185 	    const char *proxy_command)
186 {
187 	int sock = -1, attempt;
188 	struct servent *sp;
189 	struct addrinfo hints, *ai, *aitop;
190 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
191 	int gaierr;
192 	struct linger linger;
193 
194 	debug("ssh_connect: getuid %d geteuid %d anon %d",
195 	      (int) getuid(), (int) geteuid(), anonymous);
196 
197 	/* Get default port if port has not been set. */
198 	if (port == 0) {
199 		sp = getservbyname(SSH_SERVICE_NAME, "tcp");
200 		if (sp)
201 			port = ntohs(sp->s_port);
202 		else
203 			port = SSH_DEFAULT_PORT;
204 	}
205 	/* If a proxy command is given, connect using it. */
206 	if (proxy_command != NULL)
207 		return ssh_proxy_connect(host, port, original_real_uid, proxy_command);
208 
209 	/* No proxy command. */
210 
211 	memset(&hints, 0, sizeof(hints));
212 	hints.ai_family = IPv4or6;
213 	hints.ai_socktype = SOCK_STREAM;
214 	snprintf(strport, sizeof strport, "%d", port);
215 	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
216 		fatal("%s: %.100s: %s", __progname, host,
217 		    gai_strerror(gaierr));
218 
219 	/*
220 	 * Try to connect several times.  On some machines, the first time
221 	 * will sometimes fail.  In general socket code appears to behave
222 	 * quite magically on many machines.
223 	 */
224 	for (attempt = 0; attempt < connection_attempts; attempt++) {
225 		if (attempt > 0)
226 			debug("Trying again...");
227 
228 		/* Loop through addresses for this host, and try each one in
229 		   sequence until the connection succeeds. */
230 		for (ai = aitop; ai; ai = ai->ai_next) {
231 			if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
232 				continue;
233 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
234 			    ntop, sizeof(ntop), strport, sizeof(strport),
235 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
236 				error("ssh_connect: getnameinfo failed");
237 				continue;
238 			}
239 			debug("Connecting to %.200s [%.100s] port %s.",
240 				host, ntop, strport);
241 
242 			/* Create a socket for connecting. */
243 			sock = ssh_create_socket(original_real_uid,
244 			    !anonymous && geteuid() == 0 && port < IPPORT_RESERVED,
245 			    ai->ai_family);
246 			if (sock < 0)
247 				continue;
248 
249 			/* Connect to the host.  We use the user's uid in the
250 			 * hope that it will help with tcp_wrappers showing
251 			 * the remote uid as root.
252 			 */
253 			temporarily_use_uid(original_real_uid);
254 			if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) {
255 				/* Successful connection. */
256 				memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
257 				restore_uid();
258 				break;
259 			} else {
260 				debug("connect: %.100s", strerror(errno));
261 				restore_uid();
262 				/*
263 				 * Close the failed socket; there appear to
264 				 * be some problems when reusing a socket for
265 				 * which connect() has already returned an
266 				 * error.
267 				 */
268 				shutdown(sock, SHUT_RDWR);
269 				close(sock);
270 			}
271 		}
272 		if (ai)
273 			break;	/* Successful connection. */
274 
275 		/* Sleep a moment before retrying. */
276 		sleep(1);
277 	}
278 
279 	freeaddrinfo(aitop);
280 
281 	/* Return failure if we didn't get a successful connection. */
282 	if (attempt >= connection_attempts)
283 		return 0;
284 
285 	debug("Connection established.");
286 
287 	/*
288 	 * Set socket options.  We would like the socket to disappear as soon
289 	 * as it has been closed for whatever reason.
290 	 */
291 	/* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */
292 	linger.l_onoff = 1;
293 	linger.l_linger = 5;
294 	setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger));
295 
296 	/* Set the connection. */
297 	packet_set_connection(sock, sock);
298 
299 	return 1;
300 }
301 
302 /*
303  * Waits for the server identification string, and sends our own
304  * identification string.
305  */
306 void
307 ssh_exchange_identification()
308 {
309 	char buf[256], remote_version[256];	/* must be same size! */
310 	int remote_major, remote_minor, i, mismatch;
311 	int connection_in = packet_get_connection_in();
312 	int connection_out = packet_get_connection_out();
313 
314 	/* Read other side\'s version identification. */
315 	for (i = 0; i < sizeof(buf) - 1; i++) {
316 		int len = read(connection_in, &buf[i], 1);
317 		if (len < 0)
318 			fatal("ssh_exchange_identification: read: %.100s", strerror(errno));
319 		if (len != 1)
320 			fatal("ssh_exchange_identification: Connection closed by remote host");
321 		if (buf[i] == '\r') {
322 			buf[i] = '\n';
323 			buf[i + 1] = 0;
324 			continue;		/**XXX wait for \n */
325 		}
326 		if (buf[i] == '\n') {
327 			buf[i + 1] = 0;
328 			break;
329 		}
330 	}
331 	buf[sizeof(buf) - 1] = 0;
332 	server_version_string = xstrdup(buf);
333 
334 	/*
335 	 * Check that the versions match.  In future this might accept
336 	 * several versions and set appropriate flags to handle them.
337 	 */
338 	if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n",
339 	    &remote_major, &remote_minor, remote_version) != 3)
340 		fatal("Bad remote protocol version identification: '%.100s'", buf);
341 	debug("Remote protocol version %d.%d, remote software version %.100s",
342 	      remote_major, remote_minor, remote_version);
343 
344 	compat_datafellows(remote_version);
345 	mismatch = 0;
346 
347 	switch(remote_major) {
348 	case 1:
349 		if (remote_minor == 99 &&
350 		    (options.protocol & SSH_PROTO_2) &&
351 		    !(options.protocol & SSH_PROTO_1_PREFERRED)) {
352 			enable_compat20();
353 			break;
354 		}
355 		if (!(options.protocol & SSH_PROTO_1)) {
356 			mismatch = 1;
357 			break;
358 		}
359 		if (remote_minor < 3) {
360 			fatal("Remote machine has too old SSH software version.");
361 		} else if (remote_minor == 3) {
362 			/* We speak 1.3, too. */
363 			enable_compat13();
364 			if (options.forward_agent) {
365 				log("Agent forwarding disabled for protocol 1.3");
366 				options.forward_agent = 0;
367 			}
368 		}
369 		break;
370 	case 2:
371 		if (options.protocol & SSH_PROTO_2) {
372 			enable_compat20();
373 			break;
374 		}
375 		/* FALLTHROUGH */
376 	default:
377 		mismatch = 1;
378 		break;
379 	}
380 	if (mismatch)
381 		fatal("Protocol major versions differ: %d vs. %d",
382 		    (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
383 		    remote_major);
384 	if (compat20)
385 		packet_set_ssh2_format();
386 	/* Send our own protocol version identification. */
387 	snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
388 	    compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
389 	    compat20 ? PROTOCOL_MINOR_2 : PROTOCOL_MINOR_1,
390 	    SSH_VERSION);
391 	if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf))
392 		fatal("write: %.100s", strerror(errno));
393 	client_version_string = xstrdup(buf);
394 	chop(client_version_string);
395 	chop(server_version_string);
396 	debug("Local version string %.100s", client_version_string);
397 }
398 
399 int
400 read_yes_or_no(const char *prompt, int defval)
401 {
402 	char buf[1024];
403 	FILE *f;
404 	int retval = -1;
405 
406 	if (isatty(0))
407 		f = stdin;
408 	else
409 		f = fopen("/dev/tty", "rw");
410 
411 	if (f == NULL)
412 		return 0;
413 
414 	fflush(stdout);
415 
416 	while (1) {
417 		fprintf(stderr, "%s", prompt);
418 		if (fgets(buf, sizeof(buf), f) == NULL) {
419 			/* Print a newline (the prompt probably didn\'t have one). */
420 			fprintf(stderr, "\n");
421 			strlcpy(buf, "no", sizeof buf);
422 		}
423 		/* Remove newline from response. */
424 		if (strchr(buf, '\n'))
425 			*strchr(buf, '\n') = 0;
426 
427 		if (buf[0] == 0)
428 			retval = defval;
429 		if (strcmp(buf, "yes") == 0)
430 			retval = 1;
431 		if (strcmp(buf, "no") == 0)
432 			retval = 0;
433 
434 		if (retval != -1) {
435 			if (f != stdin)
436 				fclose(f);
437 			return retval;
438 		}
439 	}
440 }
441 
442 /*
443  * check whether the supplied host key is valid, return only if ok.
444  */
445 
446 void
447 check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key,
448 	const char *user_hostfile, const char *system_hostfile)
449 {
450 	Key *file_key;
451 	char *type = key_type(host_key);
452 	char *ip = NULL;
453 	char hostline[1000], *hostp;
454 	HostStatus host_status;
455 	HostStatus ip_status;
456 	int local = 0, host_ip_differ = 0;
457 	char ntop[NI_MAXHOST];
458 
459 	/*
460 	 * Force accepting of the host key for loopback/localhost. The
461 	 * problem is that if the home directory is NFS-mounted to multiple
462 	 * machines, localhost will refer to a different machine in each of
463 	 * them, and the user will get bogus HOST_CHANGED warnings.  This
464 	 * essentially disables host authentication for localhost; however,
465 	 * this is probably not a real problem.
466 	 */
467 	/**  hostaddr == 0! */
468 	switch (hostaddr->sa_family) {
469 	case AF_INET:
470 		local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
471 		break;
472 	case AF_INET6:
473 		local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
474 		break;
475 	default:
476 		local = 0;
477 		break;
478 	}
479 	if (local) {
480 		debug("Forcing accepting of host key for loopback/localhost.");
481 		return;
482 	}
483 
484 	/*
485 	 * Turn off check_host_ip for proxy connects, since
486 	 * we don't have the remote ip-address
487 	 */
488 	if (options.proxy_command != NULL && options.check_host_ip)
489 		options.check_host_ip = 0;
490 
491 	if (options.check_host_ip) {
492 		if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop),
493 		    NULL, 0, NI_NUMERICHOST) != 0)
494 			fatal("check_host_key: getnameinfo failed");
495 		ip = xstrdup(ntop);
496 	}
497 
498 	/*
499 	 * Store the host key from the known host file in here so that we can
500 	 * compare it with the key for the IP address.
501 	 */
502 	file_key = key_new(host_key->type);
503 
504 	/*
505 	 * Check if the host key is present in the user\'s list of known
506 	 * hosts or in the systemwide list.
507 	 */
508 	host_status = check_host_in_hostfile(user_hostfile, host, host_key, file_key);
509 	if (host_status == HOST_NEW)
510 		host_status = check_host_in_hostfile(system_hostfile, host, host_key, file_key);
511 	/*
512 	 * Also perform check for the ip address, skip the check if we are
513 	 * localhost or the hostname was an ip address to begin with
514 	 */
515 	if (options.check_host_ip && !local && strcmp(host, ip)) {
516 		Key *ip_key = key_new(host_key->type);
517 		ip_status = check_host_in_hostfile(user_hostfile, ip, host_key, ip_key);
518 
519 		if (ip_status == HOST_NEW)
520 			ip_status = check_host_in_hostfile(system_hostfile, ip, host_key, ip_key);
521 		if (host_status == HOST_CHANGED &&
522 		    (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key)))
523 			host_ip_differ = 1;
524 
525 		key_free(ip_key);
526 	} else
527 		ip_status = host_status;
528 
529 	key_free(file_key);
530 
531 	switch (host_status) {
532 	case HOST_OK:
533 		/* The host is known and the key matches. */
534 		debug("Host '%.200s' is known and matches the %s host key.",
535 		    host, type);
536 		if (options.check_host_ip) {
537 			if (ip_status == HOST_NEW) {
538 				if (!add_host_to_hostfile(user_hostfile, ip, host_key))
539 					log("Failed to add the %s host key for IP address '%.30s' to the list of known hosts (%.30s).",
540 					    type, ip, user_hostfile);
541 				else
542 					log("Warning: Permanently added the %s host key for IP address '%.30s' to the list of known hosts.",
543 					    type, ip);
544 			} else if (ip_status != HOST_OK)
545 				log("Warning: the %s host key for '%.200s' differs from the key for the IP address '%.30s'",
546 				    type, host, ip);
547 		}
548 		break;
549 	case HOST_NEW:
550 		/* The host is new. */
551 		if (options.strict_host_key_checking == 1) {
552 			/* User has requested strict host key checking.  We will not add the host key
553 			   automatically.  The only alternative left is to abort. */
554 			fatal("No %s host key is known for %.200s and you have requested strict checking.", type, host);
555 		} else if (options.strict_host_key_checking == 2) {
556 			/* The default */
557 			char prompt[1024];
558 			char *fp = key_fingerprint(host_key);
559 			snprintf(prompt, sizeof(prompt),
560 			    "The authenticity of host '%.200s' can't be established.\n"
561 			    "%s key fingerprint is %s.\n"
562 			    "Are you sure you want to continue connecting (yes/no)? ",
563 			    host, type, fp);
564 			if (!read_yes_or_no(prompt, -1))
565 				fatal("Aborted by user!\n");
566 		}
567 		if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) {
568 			snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
569 			hostp = hostline;
570 		} else
571 			hostp = host;
572 
573 		/* If not in strict mode, add the key automatically to the local known_hosts file. */
574 		if (!add_host_to_hostfile(user_hostfile, hostp, host_key))
575 			log("Failed to add the host to the list of known hosts (%.500s).",
576 			    user_hostfile);
577 		else
578 			log("Warning: Permanently added '%.200s' (%s) to the list of known hosts.",
579 			    hostp, type);
580 		break;
581 	case HOST_CHANGED:
582 		if (options.check_host_ip && host_ip_differ) {
583 			char *msg;
584 			if (ip_status == HOST_NEW)
585 				msg = "is unknown";
586 			else if (ip_status == HOST_OK)
587 				msg = "is unchanged";
588 			else
589 				msg = "has a different value";
590 			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
591 			error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
592 			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
593 			error("The %s host key for %s has changed,", type, host);
594 			error("and the key for the according IP address %s", ip);
595 			error("%s. This could either mean that", msg);
596 			error("DNS SPOOFING is happening or the IP address for the host");
597 			error("and its host key have changed at the same time");
598 		}
599 		/* The host key has changed. */
600 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
601 		error("@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @");
602 		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
603 		error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!");
604 		error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!");
605 		error("It is also possible that the %s host key has just been changed.", type);
606 		error("Please contact your system administrator.");
607 		error("Add correct host key in %.100s to get rid of this message.",
608 		      user_hostfile);
609 
610 		/*
611 		 * If strict host key checking is in use, the user will have
612 		 * to edit the key manually and we can only abort.
613 		 */
614 		if (options.strict_host_key_checking)
615 			fatal("%s host key for %.200s has changed and you have requested strict checking.", type, host);
616 
617 		/*
618 		 * If strict host key checking has not been requested, allow
619 		 * the connection but without password authentication or
620 		 * agent forwarding.
621 		 */
622 		if (options.password_authentication) {
623 			error("Password authentication is disabled to avoid trojan horses.");
624 			options.password_authentication = 0;
625 		}
626 		if (options.forward_agent) {
627 			error("Agent forwarding is disabled to avoid trojan horses.");
628 			options.forward_agent = 0;
629 		}
630 		/*
631 		 * XXX Should permit the user to change to use the new id.
632 		 * This could be done by converting the host key to an
633 		 * identifying sentence, tell that the host identifies itself
634 		 * by that sentence, and ask the user if he/she whishes to
635 		 * accept the authentication.
636 		 */
637 		break;
638 	}
639 	if (options.check_host_ip)
640 		xfree(ip);
641 }
642 
643 #ifdef KRB5
644 int
645 try_krb5_authentication(krb5_context *context, krb5_auth_context *auth_context)
646 {
647   krb5_error_code problem;
648   const char *tkfile;
649   struct stat buf;
650   krb5_ccache ccache = NULL;
651   krb5_creds req_creds;
652   krb5_creds *new_creds = NULL;
653   const char *remotehost;
654   krb5_data ap;
655   int type, payload_len;
656   krb5_ap_rep_enc_part *reply = NULL;
657   int ret;
658 
659   memset(&ap, 0, sizeof(ap));
660 
661   problem = krb5_init_context(context);
662   if (problem) {
663      ret = 0;
664      goto out;
665   }
666 
667   tkfile = krb5_cc_default_name(*context);
668   if (strncmp(tkfile, "FILE:", 5) == 0)
669      tkfile += 5;
670 
671   if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) {
672     debug("Kerberos V5: could not get default ccache (permission denied).");
673     ret = 0;
674     goto out;
675   }
676 
677   problem = krb5_cc_default(*context, &ccache);
678   if (problem) {
679      ret = 0;
680      goto out;
681   }
682 
683   memset(&req_creds, 0, sizeof(req_creds));
684 
685   remotehost = get_canonical_hostname();
686 
687   problem = krb5_sname_to_principal(*context, remotehost,
688       				    "host", KRB5_NT_SRV_HST,
689 				    &req_creds.server);
690   if (problem) {
691      ret = 0;
692      goto out;
693 
694   }
695 
696   problem = krb5_cc_get_principal(*context, ccache, &req_creds.client);
697   if (problem) {
698      ret = 0;
699      goto out;
700   }
701 
702   /* creds.session.keytype=ETYPE_DES_CBC_CRC; */
703 
704   problem = krb5_get_credentials(*context, 0, ccache, &req_creds, &new_creds);
705   if (problem) {
706      ret = 0;
707      goto out;
708   }
709 
710   problem = krb5_auth_con_init(*context, auth_context);
711   if (problem) {
712      ret = 0;
713      goto out;
714   }
715 
716   /* krb5_auth_con_setflags(ssh_context, auth_context,
717                                   KRB5_AUTH_CONTEXT_RET_TIME);
718 	*/
719   problem = krb5_mk_req_extended(*context, auth_context,
720     AP_OPTS_MUTUAL_REQUIRED /*| AP_OPTS_USE_SUBKEY*/ ,
721                                 NULL, new_creds, &ap);
722   if (problem) {
723      ret = 0;
724      goto out;
725   }
726 
727   packet_start(SSH_CMSG_AUTH_KRB5);
728   packet_put_string((char *) ap.data, ap.length);
729   packet_send();
730   packet_write_wait();
731 
732   xfree(ap.data);
733   ap.length = 0;
734 
735   type = packet_read(&payload_len);
736    switch (type) {
737         case SSH_SMSG_FAILURE:
738                 /* Should really be SSH_SMSG_AUTH_KRB5_FAILURE */
739                 debug("Kerberos V5 authentication failed.");
740                 ret = 0;
741                 break;
742 
743          case SSH_SMSG_AUTH_KRB5_RESPONSE:
744                 /* SSH_SMSG_AUTH_KRB5_SUCCESS */
745                 debug("Kerberos V5 authentication accepted.");
746 
747                 /* Get server's response. */
748                 ap.data = packet_get_string((unsigned int *) &ap.length);
749 
750                 packet_integrity_check(payload_len, 4 + ap.length, type);
751                 /* XXX je to dobre? */
752 
753                 problem = krb5_rd_rep(*context, *auth_context, &ap, &reply);
754                 if (problem) {
755 		   ret = 0;
756                 }
757 		ret = 1;
758 		break;
759 
760 	default:
761 		packet_disconnect("Protocol error on Kerberos V5 response: %d", type);
762 		ret = 0;
763 		break;
764 
765    }
766 
767 out:
768    if (req_creds.server != NULL)
769       krb5_free_principal(*context, req_creds.server);
770    if (req_creds.client != NULL)
771       krb5_free_principal(*context, req_creds.client);
772    if (new_creds != NULL)
773       krb5_free_creds(*context, new_creds);
774    if (ccache != NULL)
775       krb5_cc_close(*context, ccache);
776    if (reply != NULL)
777       krb5_free_ap_rep_enc_part(*context, reply);
778    if (ap.length > 0)
779       krb5_data_free(&ap);
780 
781    return ret;
782 
783 }
784 
785 void
786 send_krb5_tgt(krb5_context context, krb5_auth_context auth_context)
787 {
788   int fd;
789   int type, payload_len;
790   krb5_error_code problem;
791   krb5_data outbuf;
792   krb5_ccache ccache = NULL;
793   krb5_creds creds;
794   krb5_kdc_flags flags;
795   const char* remotehost = get_canonical_hostname();
796 
797   memset(&creds, 0, sizeof(creds));
798   memset(&outbuf, 0, sizeof(outbuf));
799 
800   fd = packet_get_connection_in();
801   problem = krb5_auth_con_setaddrs_from_fd(context, auth_context, &fd);
802   if (problem) {
803      goto out;
804   }
805 
806 #if 0
807   tkfile = krb5_cc_default_name(context);
808   if (strncmp(tkfile, "FILE:", 5) == 0)
809      tkfile += 5;
810 
811   if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) {
812      debug("Kerberos V5: could not get default ccache (permission denied).");
813      goto out;
814   }
815 #endif
816 
817   problem = krb5_cc_default(context, &ccache);
818   if (problem) {
819      goto out;
820   }
821 
822   problem = krb5_cc_get_principal(context, ccache, &creds.client);
823   if (problem) {
824      goto out;
825   }
826 
827   problem = krb5_build_principal(context, &creds.server,
828 	                         strlen(creds.client->realm),
829 				 creds.client->realm,
830 				 "krbtgt",
831 				 creds.client->realm,
832 				 NULL);
833   if (problem) {
834      goto out;
835   }
836 
837   creds.times.endtime = 0;
838 
839   flags.i = 0;
840   flags.b.forwarded = 1;
841   flags.b.forwardable = krb5_config_get_bool(context,  NULL,
842 	                  "libdefaults", "forwardable", NULL);
843 
844   problem = krb5_get_forwarded_creds (context,
845 	                              auth_context,
846 				      ccache,
847 				      flags.i,
848 				      remotehost,
849 				      &creds,
850 				      &outbuf);
851   if (problem) {
852      goto out;
853   }
854 
855   packet_start(SSH_CMSG_HAVE_KRB5_TGT);
856   packet_put_string((char *)outbuf.data, outbuf.length);
857   packet_send();
858   packet_write_wait();
859 
860   type = packet_read(&payload_len);
861   switch (type) {
862      case SSH_SMSG_SUCCESS:
863 	break;
864      case SSH_SMSG_FAILURE:
865 	break;
866      default:
867 	break;
868   }
869 
870 out:
871   if (creds.client)
872      krb5_free_principal(context, creds.client);
873   if (creds.server)
874      krb5_free_principal(context, creds.server);
875   if (ccache)
876      krb5_cc_close(context, ccache);
877   if (outbuf.data)
878      xfree(outbuf.data);
879 
880   return;
881 }
882 #endif /* KRB5 */
883 
884 /*
885  * Starts a dialog with the server, and authenticates the current user on the
886  * server.  This does not need any extra privileges.  The basic connection
887  * to the server must already have been established before this is called.
888  * If login fails, this function prints an error and never returns.
889  * This function does not require super-user privileges.
890  */
891 void
892 ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost,
893     struct sockaddr *hostaddr, uid_t original_real_uid)
894 {
895 	struct passwd *pw;
896 	char *host, *cp;
897 	char *server_user, *local_user;
898 
899 	/* Get local user name.  Use it as server user if no user name was given. */
900 	pw = getpwuid(original_real_uid);
901 	if (!pw)
902 		fatal("User id %d not found from user database.", original_real_uid);
903 	local_user = xstrdup(pw->pw_name);
904 	server_user = options.user ? options.user : local_user;
905 
906 	/* Convert the user-supplied hostname into all lowercase. */
907 	host = xstrdup(orighost);
908 	for (cp = host; *cp; cp++)
909 		if (isupper(*cp))
910 			*cp = tolower(*cp);
911 
912 	/* Exchange protocol version identification strings with the server. */
913 	ssh_exchange_identification();
914 
915 	/* Put the connection into non-blocking mode. */
916 	packet_set_nonblocking();
917 
918 	/* key exchange */
919 	/* authenticate user */
920 	if (compat20) {
921 		ssh_kex2(host, hostaddr);
922 		ssh_userauth2(server_user, host);
923 	} else {
924 		ssh_kex(host, hostaddr);
925 		ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key);
926 	}
927 }
928