sshconnect.c revision 162856
1/* $OpenBSD: sshconnect.c,v 1.199 2006/08/03 03:34:42 deraadt 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 18#include <sys/types.h> 19#include <sys/wait.h> 20#include <sys/stat.h> 21#include <sys/socket.h> 22#ifdef HAVE_SYS_TIME_H 23# include <sys/time.h> 24#endif 25 26#include <netinet/in.h> 27#include <arpa/inet.h> 28 29#include <ctype.h> 30#include <errno.h> 31#include <netdb.h> 32#ifdef HAVE_PATHS_H 33#include <paths.h> 34#endif 35#include <pwd.h> 36#include <stdarg.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40#include <unistd.h> 41 42#include "xmalloc.h" 43#include "key.h" 44#include "hostfile.h" 45#include "ssh.h" 46#include "rsa.h" 47#include "buffer.h" 48#include "packet.h" 49#include "uidswap.h" 50#include "compat.h" 51#include "key.h" 52#include "sshconnect.h" 53#include "hostfile.h" 54#include "log.h" 55#include "readconf.h" 56#include "atomicio.h" 57#include "misc.h" 58#include "dns.h" 59#include "version.h" 60 61char *client_version_string = NULL; 62char *server_version_string = NULL; 63 64static int matching_host_key_dns = 0; 65 66/* import */ 67extern Options options; 68extern char *__progname; 69extern uid_t original_real_uid; 70extern uid_t original_effective_uid; 71extern pid_t proxy_command_pid; 72 73#ifndef INET6_ADDRSTRLEN /* for non IPv6 machines */ 74#define INET6_ADDRSTRLEN 46 75#endif 76 77static int show_other_keys(const char *, Key *); 78static void warn_changed_key(Key *); 79 80/* 81 * Connect to the given ssh server using a proxy command. 82 */ 83static int 84ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) 85{ 86 char *command_string, *tmp; 87 int pin[2], pout[2]; 88 pid_t pid; 89 char strport[NI_MAXSERV]; 90 91 /* Convert the port number into a string. */ 92 snprintf(strport, sizeof strport, "%hu", port); 93 94 /* 95 * Build the final command string in the buffer by making the 96 * appropriate substitutions to the given proxy command. 97 * 98 * Use "exec" to avoid "sh -c" processes on some platforms 99 * (e.g. Solaris) 100 */ 101 xasprintf(&tmp, "exec %s", proxy_command); 102 command_string = percent_expand(tmp, "h", host, 103 "p", strport, (char *)NULL); 104 xfree(tmp); 105 106 /* Create pipes for communicating with the proxy. */ 107 if (pipe(pin) < 0 || pipe(pout) < 0) 108 fatal("Could not create pipes to communicate with the proxy: %.100s", 109 strerror(errno)); 110 111 debug("Executing proxy command: %.500s", command_string); 112 113 /* Fork and execute the proxy command. */ 114 if ((pid = fork()) == 0) { 115 char *argv[10]; 116 117 /* Child. Permanently give up superuser privileges. */ 118 permanently_drop_suid(original_real_uid); 119 120 /* Redirect stdin and stdout. */ 121 close(pin[1]); 122 if (pin[0] != 0) { 123 if (dup2(pin[0], 0) < 0) 124 perror("dup2 stdin"); 125 close(pin[0]); 126 } 127 close(pout[0]); 128 if (dup2(pout[1], 1) < 0) 129 perror("dup2 stdout"); 130 /* Cannot be 1 because pin allocated two descriptors. */ 131 close(pout[1]); 132 133 /* Stderr is left as it is so that error messages get 134 printed on the user's terminal. */ 135 argv[0] = _PATH_BSHELL; 136 argv[1] = "-c"; 137 argv[2] = command_string; 138 argv[3] = NULL; 139 140 /* Execute the proxy command. Note that we gave up any 141 extra privileges above. */ 142 execv(argv[0], argv); 143 perror(argv[0]); 144 exit(1); 145 } 146 /* Parent. */ 147 if (pid < 0) 148 fatal("fork failed: %.100s", strerror(errno)); 149 else 150 proxy_command_pid = pid; /* save pid to clean up later */ 151 152 /* Close child side of the descriptors. */ 153 close(pin[0]); 154 close(pout[1]); 155 156 /* Free the command name. */ 157 xfree(command_string); 158 159 /* Set the connection file descriptors. */ 160 packet_set_connection(pout[0], pin[1]); 161 162 /* Indicate OK return */ 163 return 0; 164} 165 166/* 167 * Creates a (possibly privileged) socket for use as the ssh connection. 168 */ 169static int 170ssh_create_socket(int privileged, struct addrinfo *ai) 171{ 172 int sock, gaierr; 173 struct addrinfo hints, *res; 174 175 /* 176 * If we are running as root and want to connect to a privileged 177 * port, bind our own socket to a privileged port. 178 */ 179 if (privileged) { 180 int p = IPPORT_RESERVED - 1; 181 PRIV_START; 182 sock = rresvport_af(&p, ai->ai_family); 183 PRIV_END; 184 if (sock < 0) 185 error("rresvport: af=%d %.100s", ai->ai_family, 186 strerror(errno)); 187 else 188 debug("Allocated local port %d.", p); 189 return sock; 190 } 191 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 192 if (sock < 0) 193 error("socket: %.100s", strerror(errno)); 194 195 /* Bind the socket to an alternative local IP address */ 196 if (options.bind_address == NULL) 197 return sock; 198 199 memset(&hints, 0, sizeof(hints)); 200 hints.ai_family = ai->ai_family; 201 hints.ai_socktype = ai->ai_socktype; 202 hints.ai_protocol = ai->ai_protocol; 203 hints.ai_flags = AI_PASSIVE; 204 gaierr = getaddrinfo(options.bind_address, "0", &hints, &res); 205 if (gaierr) { 206 error("getaddrinfo: %s: %s", options.bind_address, 207 gai_strerror(gaierr)); 208 close(sock); 209 return -1; 210 } 211 if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 212 error("bind: %s: %s", options.bind_address, strerror(errno)); 213 close(sock); 214 freeaddrinfo(res); 215 return -1; 216 } 217 freeaddrinfo(res); 218 return sock; 219} 220 221static int 222timeout_connect(int sockfd, const struct sockaddr *serv_addr, 223 socklen_t addrlen, int timeout) 224{ 225 fd_set *fdset; 226 struct timeval tv; 227 socklen_t optlen; 228 int optval, rc, result = -1; 229 230 if (timeout <= 0) 231 return (connect(sockfd, serv_addr, addrlen)); 232 233 set_nonblock(sockfd); 234 rc = connect(sockfd, serv_addr, addrlen); 235 if (rc == 0) { 236 unset_nonblock(sockfd); 237 return (0); 238 } 239 if (errno != EINPROGRESS) 240 return (-1); 241 242 fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), 243 sizeof(fd_mask)); 244 FD_SET(sockfd, fdset); 245 tv.tv_sec = timeout; 246 tv.tv_usec = 0; 247 248 for (;;) { 249 rc = select(sockfd + 1, NULL, fdset, NULL, &tv); 250 if (rc != -1 || errno != EINTR) 251 break; 252 } 253 254 switch (rc) { 255 case 0: 256 /* Timed out */ 257 errno = ETIMEDOUT; 258 break; 259 case -1: 260 /* Select error */ 261 debug("select: %s", strerror(errno)); 262 break; 263 case 1: 264 /* Completed or failed */ 265 optval = 0; 266 optlen = sizeof(optval); 267 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, 268 &optlen) == -1) { 269 debug("getsockopt: %s", strerror(errno)); 270 break; 271 } 272 if (optval != 0) { 273 errno = optval; 274 break; 275 } 276 result = 0; 277 unset_nonblock(sockfd); 278 break; 279 default: 280 /* Should not occur */ 281 fatal("Bogus return (%d) from select()", rc); 282 } 283 284 xfree(fdset); 285 return (result); 286} 287 288/* 289 * Opens a TCP/IP connection to the remote server on the given host. 290 * The address of the remote host will be returned in hostaddr. 291 * If port is 0, the default port will be used. If needpriv is true, 292 * a privileged port will be allocated to make the connection. 293 * This requires super-user privileges if needpriv is true. 294 * Connection_attempts specifies the maximum number of tries (one per 295 * second). If proxy_command is non-NULL, it specifies the command (with %h 296 * and %p substituted for host and port, respectively) to use to contact 297 * the daemon. 298 */ 299int 300ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 301 u_short port, int family, int connection_attempts, 302 int needpriv, const char *proxy_command) 303{ 304 int gaierr; 305 int on = 1; 306 int sock = -1, attempt; 307 char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 308 struct addrinfo hints, *ai, *aitop; 309 310 debug2("ssh_connect: needpriv %d", needpriv); 311 312 /* If a proxy command is given, connect using it. */ 313 if (proxy_command != NULL) 314 return ssh_proxy_connect(host, port, proxy_command); 315 316 /* No proxy command. */ 317 318 memset(&hints, 0, sizeof(hints)); 319 hints.ai_family = family; 320 hints.ai_socktype = SOCK_STREAM; 321 snprintf(strport, sizeof strport, "%u", port); 322 if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 323 fatal("%s: %.100s: %s", __progname, host, 324 gai_strerror(gaierr)); 325 326 for (attempt = 0; attempt < connection_attempts; attempt++) { 327 if (attempt > 0) 328 debug("Trying again..."); 329 330 /* 331 * Loop through addresses for this host, and try each one in 332 * sequence until the connection succeeds. 333 */ 334 for (ai = aitop; ai; ai = ai->ai_next) { 335 if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 336 continue; 337 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 338 ntop, sizeof(ntop), strport, sizeof(strport), 339 NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 340 error("ssh_connect: getnameinfo failed"); 341 continue; 342 } 343 debug("Connecting to %.200s [%.100s] port %s.", 344 host, ntop, strport); 345 346 /* Create a socket for connecting. */ 347 sock = ssh_create_socket(needpriv, ai); 348 if (sock < 0) 349 /* Any error is already output */ 350 continue; 351 352 if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 353 options.connection_timeout) >= 0) { 354 /* Successful connection. */ 355 memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 356 break; 357 } else { 358 debug("connect to address %s port %s: %s", 359 ntop, strport, strerror(errno)); 360 close(sock); 361 sock = -1; 362 } 363 } 364 if (sock != -1) 365 break; /* Successful connection. */ 366 367 /* Sleep a moment before retrying. */ 368 sleep(1); 369 } 370 371 freeaddrinfo(aitop); 372 373 /* Return failure if we didn't get a successful connection. */ 374 if (sock == -1) { 375 error("ssh: connect to host %s port %s: %s", 376 host, strport, strerror(errno)); 377 return (-1); 378 } 379 380 debug("Connection established."); 381 382 /* Set SO_KEEPALIVE if requested. */ 383 if (options.tcp_keep_alive && 384 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 385 sizeof(on)) < 0) 386 error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 387 388 /* Set the connection. */ 389 packet_set_connection(sock, sock); 390 391 return 0; 392} 393 394/* 395 * Waits for the server identification string, and sends our own 396 * identification string. 397 */ 398static void 399ssh_exchange_identification(void) 400{ 401 char buf[256], remote_version[256]; /* must be same size! */ 402 int remote_major, remote_minor, mismatch; 403 int connection_in = packet_get_connection_in(); 404 int connection_out = packet_get_connection_out(); 405 int minor1 = PROTOCOL_MINOR_1; 406 u_int i, n; 407 408 /* Read other side's version identification. */ 409 for (n = 0;;) { 410 for (i = 0; i < sizeof(buf) - 1; i++) { 411 size_t len = atomicio(read, connection_in, &buf[i], 1); 412 413 if (len != 1 && errno == EPIPE) 414 fatal("ssh_exchange_identification: Connection closed by remote host"); 415 else if (len != 1) 416 fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); 417 if (buf[i] == '\r') { 418 buf[i] = '\n'; 419 buf[i + 1] = 0; 420 continue; /**XXX wait for \n */ 421 } 422 if (buf[i] == '\n') { 423 buf[i + 1] = 0; 424 break; 425 } 426 if (++n > 65536) 427 fatal("ssh_exchange_identification: No banner received"); 428 } 429 buf[sizeof(buf) - 1] = 0; 430 if (strncmp(buf, "SSH-", 4) == 0) 431 break; 432 debug("ssh_exchange_identification: %s", buf); 433 } 434 server_version_string = xstrdup(buf); 435 436 /* 437 * Check that the versions match. In future this might accept 438 * several versions and set appropriate flags to handle them. 439 */ 440 if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 441 &remote_major, &remote_minor, remote_version) != 3) 442 fatal("Bad remote protocol version identification: '%.100s'", buf); 443 debug("Remote protocol version %d.%d, remote software version %.100s", 444 remote_major, remote_minor, remote_version); 445 446 compat_datafellows(remote_version); 447 mismatch = 0; 448 449 switch (remote_major) { 450 case 1: 451 if (remote_minor == 99 && 452 (options.protocol & SSH_PROTO_2) && 453 !(options.protocol & SSH_PROTO_1_PREFERRED)) { 454 enable_compat20(); 455 break; 456 } 457 if (!(options.protocol & SSH_PROTO_1)) { 458 mismatch = 1; 459 break; 460 } 461 if (remote_minor < 3) { 462 fatal("Remote machine has too old SSH software version."); 463 } else if (remote_minor == 3 || remote_minor == 4) { 464 /* We speak 1.3, too. */ 465 enable_compat13(); 466 minor1 = 3; 467 if (options.forward_agent) { 468 logit("Agent forwarding disabled for protocol 1.3"); 469 options.forward_agent = 0; 470 } 471 } 472 break; 473 case 2: 474 if (options.protocol & SSH_PROTO_2) { 475 enable_compat20(); 476 break; 477 } 478 /* FALLTHROUGH */ 479 default: 480 mismatch = 1; 481 break; 482 } 483 if (mismatch) 484 fatal("Protocol major versions differ: %d vs. %d", 485 (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 486 remote_major); 487 /* Send our own protocol version identification. */ 488 snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 489 compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 490 compat20 ? PROTOCOL_MINOR_2 : minor1, 491 SSH_VERSION); 492 if (atomicio(vwrite, connection_out, buf, strlen(buf)) != strlen(buf)) 493 fatal("write: %.100s", strerror(errno)); 494 client_version_string = xstrdup(buf); 495 chop(client_version_string); 496 chop(server_version_string); 497 debug("Local version string %.100s", client_version_string); 498} 499 500/* defaults to 'no' */ 501static int 502confirm(const char *prompt) 503{ 504 const char *msg, *again = "Please type 'yes' or 'no': "; 505 char *p; 506 int ret = -1; 507 508 if (options.batch_mode) 509 return 0; 510 for (msg = prompt;;msg = again) { 511 p = read_passphrase(msg, RP_ECHO); 512 if (p == NULL || 513 (p[0] == '\0') || (p[0] == '\n') || 514 strncasecmp(p, "no", 2) == 0) 515 ret = 0; 516 if (p && strncasecmp(p, "yes", 3) == 0) 517 ret = 1; 518 if (p) 519 xfree(p); 520 if (ret != -1) 521 return ret; 522 } 523} 524 525/* 526 * check whether the supplied host key is valid, return -1 if the key 527 * is not valid. the user_hostfile will not be updated if 'readonly' is true. 528 */ 529#define RDRW 0 530#define RDONLY 1 531#define ROQUIET 2 532static int 533check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 534 Key *host_key, int readonly, const char *user_hostfile, 535 const char *system_hostfile) 536{ 537 Key *file_key; 538 const char *type = key_type(host_key); 539 char *ip = NULL, *host = NULL; 540 char hostline[1000], *hostp, *fp; 541 HostStatus host_status; 542 HostStatus ip_status; 543 int r, local = 0, host_ip_differ = 0; 544 int salen; 545 char ntop[NI_MAXHOST]; 546 char msg[1024]; 547 int len, host_line, ip_line; 548 const char *host_file = NULL, *ip_file = NULL; 549 550 /* 551 * Force accepting of the host key for loopback/localhost. The 552 * problem is that if the home directory is NFS-mounted to multiple 553 * machines, localhost will refer to a different machine in each of 554 * them, and the user will get bogus HOST_CHANGED warnings. This 555 * essentially disables host authentication for localhost; however, 556 * this is probably not a real problem. 557 */ 558 /** hostaddr == 0! */ 559 switch (hostaddr->sa_family) { 560 case AF_INET: 561 local = (ntohl(((struct sockaddr_in *)hostaddr)-> 562 sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 563 salen = sizeof(struct sockaddr_in); 564 break; 565 case AF_INET6: 566 local = IN6_IS_ADDR_LOOPBACK( 567 &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 568 salen = sizeof(struct sockaddr_in6); 569 break; 570 default: 571 local = 0; 572 salen = sizeof(struct sockaddr_storage); 573 break; 574 } 575 if (options.no_host_authentication_for_localhost == 1 && local && 576 options.host_key_alias == NULL) { 577 debug("Forcing accepting of host key for " 578 "loopback/localhost."); 579 return 0; 580 } 581 582 /* 583 * We don't have the remote ip-address for connections 584 * using a proxy command 585 */ 586 if (options.proxy_command == NULL) { 587 if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop), 588 NULL, 0, NI_NUMERICHOST) != 0) 589 fatal("check_host_key: getnameinfo failed"); 590 ip = put_host_port(ntop, port); 591 } else { 592 ip = xstrdup("<no hostip for proxy command>"); 593 } 594 /* 595 * Turn off check_host_ip if the connection is to localhost, via proxy 596 * command or if we don't have a hostname to compare with 597 */ 598 if (options.check_host_ip && (local || 599 strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 600 options.check_host_ip = 0; 601 602 /* 603 * Allow the user to record the key under a different name or 604 * differentiate a non-standard port. This is useful for ssh 605 * tunneling over forwarded connections or if you run multiple 606 * sshd's on different ports on the same machine. 607 */ 608 if (options.host_key_alias != NULL) { 609 host = xstrdup(options.host_key_alias); 610 debug("using hostkeyalias: %s", host); 611 } else { 612 host = put_host_port(hostname, port); 613 } 614 615 /* 616 * Store the host key from the known host file in here so that we can 617 * compare it with the key for the IP address. 618 */ 619 file_key = key_new(host_key->type); 620 621 /* 622 * Check if the host key is present in the user's list of known 623 * hosts or in the systemwide list. 624 */ 625 host_file = user_hostfile; 626 host_status = check_host_in_hostfile(host_file, host, host_key, 627 file_key, &host_line); 628 if (host_status == HOST_NEW) { 629 host_file = system_hostfile; 630 host_status = check_host_in_hostfile(host_file, host, host_key, 631 file_key, &host_line); 632 } 633 /* 634 * Also perform check for the ip address, skip the check if we are 635 * localhost or the hostname was an ip address to begin with 636 */ 637 if (options.check_host_ip) { 638 Key *ip_key = key_new(host_key->type); 639 640 ip_file = user_hostfile; 641 ip_status = check_host_in_hostfile(ip_file, ip, host_key, 642 ip_key, &ip_line); 643 if (ip_status == HOST_NEW) { 644 ip_file = system_hostfile; 645 ip_status = check_host_in_hostfile(ip_file, ip, 646 host_key, ip_key, &ip_line); 647 } 648 if (host_status == HOST_CHANGED && 649 (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) 650 host_ip_differ = 1; 651 652 key_free(ip_key); 653 } else 654 ip_status = host_status; 655 656 key_free(file_key); 657 658 switch (host_status) { 659 case HOST_OK: 660 /* The host is known and the key matches. */ 661 debug("Host '%.200s' is known and matches the %s host key.", 662 host, type); 663 debug("Found key in %s:%d", host_file, host_line); 664 if (options.check_host_ip && ip_status == HOST_NEW) { 665 if (readonly) 666 logit("%s host key for IP address " 667 "'%.128s' not in list of known hosts.", 668 type, ip); 669 else if (!add_host_to_hostfile(user_hostfile, ip, 670 host_key, options.hash_known_hosts)) 671 logit("Failed to add the %s host key for IP " 672 "address '%.128s' to the list of known " 673 "hosts (%.30s).", type, ip, user_hostfile); 674 else 675 logit("Warning: Permanently added the %s host " 676 "key for IP address '%.128s' to the list " 677 "of known hosts.", type, ip); 678 } 679 break; 680 case HOST_NEW: 681 if (options.host_key_alias == NULL && port != 0 && 682 port != SSH_DEFAULT_PORT) { 683 debug("checking without port identifier"); 684 if (check_host_key(hostname, hostaddr, 0, host_key, 2, 685 user_hostfile, system_hostfile) == 0) { 686 debug("found matching key w/out port"); 687 break; 688 } 689 } 690 if (readonly) 691 goto fail; 692 /* The host is new. */ 693 if (options.strict_host_key_checking == 1) { 694 /* 695 * User has requested strict host key checking. We 696 * will not add the host key automatically. The only 697 * alternative left is to abort. 698 */ 699 error("No %s host key is known for %.200s and you " 700 "have requested strict checking.", type, host); 701 goto fail; 702 } else if (options.strict_host_key_checking == 2) { 703 char msg1[1024], msg2[1024]; 704 705 if (show_other_keys(host, host_key)) 706 snprintf(msg1, sizeof(msg1), 707 "\nbut keys of different type are already" 708 " known for this host."); 709 else 710 snprintf(msg1, sizeof(msg1), "."); 711 /* The default */ 712 fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 713 msg2[0] = '\0'; 714 if (options.verify_host_key_dns) { 715 if (matching_host_key_dns) 716 snprintf(msg2, sizeof(msg2), 717 "Matching host key fingerprint" 718 " found in DNS.\n"); 719 else 720 snprintf(msg2, sizeof(msg2), 721 "No matching host key fingerprint" 722 " found in DNS.\n"); 723 } 724 snprintf(msg, sizeof(msg), 725 "The authenticity of host '%.200s (%s)' can't be " 726 "established%s\n" 727 "%s key fingerprint is %s.\n%s" 728 "Are you sure you want to continue connecting " 729 "(yes/no)? ", 730 host, ip, msg1, type, fp, msg2); 731 xfree(fp); 732 if (!confirm(msg)) 733 goto fail; 734 } 735 /* 736 * If not in strict mode, add the key automatically to the 737 * local known_hosts file. 738 */ 739 if (options.check_host_ip && ip_status == HOST_NEW) { 740 snprintf(hostline, sizeof(hostline), "%s,%s", 741 host, ip); 742 hostp = hostline; 743 if (options.hash_known_hosts) { 744 /* Add hash of host and IP separately */ 745 r = add_host_to_hostfile(user_hostfile, host, 746 host_key, options.hash_known_hosts) && 747 add_host_to_hostfile(user_hostfile, ip, 748 host_key, options.hash_known_hosts); 749 } else { 750 /* Add unhashed "host,ip" */ 751 r = add_host_to_hostfile(user_hostfile, 752 hostline, host_key, 753 options.hash_known_hosts); 754 } 755 } else { 756 r = add_host_to_hostfile(user_hostfile, host, host_key, 757 options.hash_known_hosts); 758 hostp = host; 759 } 760 761 if (!r) 762 logit("Failed to add the host to the list of known " 763 "hosts (%.500s).", user_hostfile); 764 else 765 logit("Warning: Permanently added '%.200s' (%s) to the " 766 "list of known hosts.", hostp, type); 767 break; 768 case HOST_CHANGED: 769 if (readonly == ROQUIET) 770 goto fail; 771 if (options.check_host_ip && host_ip_differ) { 772 char *key_msg; 773 if (ip_status == HOST_NEW) 774 key_msg = "is unknown"; 775 else if (ip_status == HOST_OK) 776 key_msg = "is unchanged"; 777 else 778 key_msg = "has a different value"; 779 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 780 error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 781 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 782 error("The %s host key for %s has changed,", type, host); 783 error("and the key for the according IP address %s", ip); 784 error("%s. This could either mean that", key_msg); 785 error("DNS SPOOFING is happening or the IP address for the host"); 786 error("and its host key have changed at the same time."); 787 if (ip_status != HOST_NEW) 788 error("Offending key for IP in %s:%d", ip_file, ip_line); 789 } 790 /* The host key has changed. */ 791 warn_changed_key(host_key); 792 error("Add correct host key in %.100s to get rid of this message.", 793 user_hostfile); 794 error("Offending key in %s:%d", host_file, host_line); 795 796 /* 797 * If strict host key checking is in use, the user will have 798 * to edit the key manually and we can only abort. 799 */ 800 if (options.strict_host_key_checking) { 801 error("%s host key for %.200s has changed and you have " 802 "requested strict checking.", type, host); 803 goto fail; 804 } 805 806 /* 807 * If strict host key checking has not been requested, allow 808 * the connection but without MITM-able authentication or 809 * forwarding. 810 */ 811 if (options.password_authentication) { 812 error("Password authentication is disabled to avoid " 813 "man-in-the-middle attacks."); 814 options.password_authentication = 0; 815 } 816 if (options.kbd_interactive_authentication) { 817 error("Keyboard-interactive authentication is disabled" 818 " to avoid man-in-the-middle attacks."); 819 options.kbd_interactive_authentication = 0; 820 options.challenge_response_authentication = 0; 821 } 822 if (options.challenge_response_authentication) { 823 error("Challenge/response authentication is disabled" 824 " to avoid man-in-the-middle attacks."); 825 options.challenge_response_authentication = 0; 826 } 827 if (options.forward_agent) { 828 error("Agent forwarding is disabled to avoid " 829 "man-in-the-middle attacks."); 830 options.forward_agent = 0; 831 } 832 if (options.forward_x11) { 833 error("X11 forwarding is disabled to avoid " 834 "man-in-the-middle attacks."); 835 options.forward_x11 = 0; 836 } 837 if (options.num_local_forwards > 0 || 838 options.num_remote_forwards > 0) { 839 error("Port forwarding is disabled to avoid " 840 "man-in-the-middle attacks."); 841 options.num_local_forwards = 842 options.num_remote_forwards = 0; 843 } 844 if (options.tun_open != SSH_TUNMODE_NO) { 845 error("Tunnel forwarding is disabled to avoid " 846 "man-in-the-middle attacks."); 847 options.tun_open = SSH_TUNMODE_NO; 848 } 849 /* 850 * XXX Should permit the user to change to use the new id. 851 * This could be done by converting the host key to an 852 * identifying sentence, tell that the host identifies itself 853 * by that sentence, and ask the user if he/she whishes to 854 * accept the authentication. 855 */ 856 break; 857 case HOST_FOUND: 858 fatal("internal error"); 859 break; 860 } 861 862 if (options.check_host_ip && host_status != HOST_CHANGED && 863 ip_status == HOST_CHANGED) { 864 snprintf(msg, sizeof(msg), 865 "Warning: the %s host key for '%.200s' " 866 "differs from the key for the IP address '%.128s'" 867 "\nOffending key for IP in %s:%d", 868 type, host, ip, ip_file, ip_line); 869 if (host_status == HOST_OK) { 870 len = strlen(msg); 871 snprintf(msg + len, sizeof(msg) - len, 872 "\nMatching host key in %s:%d", 873 host_file, host_line); 874 } 875 if (options.strict_host_key_checking == 1) { 876 logit("%s", msg); 877 error("Exiting, you have requested strict checking."); 878 goto fail; 879 } else if (options.strict_host_key_checking == 2) { 880 strlcat(msg, "\nAre you sure you want " 881 "to continue connecting (yes/no)? ", sizeof(msg)); 882 if (!confirm(msg)) 883 goto fail; 884 } else { 885 logit("%s", msg); 886 } 887 } 888 889 xfree(ip); 890 xfree(host); 891 return 0; 892 893fail: 894 xfree(ip); 895 xfree(host); 896 return -1; 897} 898 899/* returns 0 if key verifies or -1 if key does NOT verify */ 900int 901verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 902{ 903 struct stat st; 904 int flags = 0; 905 906 if (options.verify_host_key_dns && 907 verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { 908 909 if (flags & DNS_VERIFY_FOUND) { 910 911 if (options.verify_host_key_dns == 1 && 912 flags & DNS_VERIFY_MATCH && 913 flags & DNS_VERIFY_SECURE) 914 return 0; 915 916 if (flags & DNS_VERIFY_MATCH) { 917 matching_host_key_dns = 1; 918 } else { 919 warn_changed_key(host_key); 920 error("Update the SSHFP RR in DNS with the new " 921 "host key to get rid of this message."); 922 } 923 } 924 } 925 926 /* return ok if the key can be found in an old keyfile */ 927 if (stat(options.system_hostfile2, &st) == 0 || 928 stat(options.user_hostfile2, &st) == 0) { 929 if (check_host_key(host, hostaddr, options.port, host_key, 930 RDONLY, options.user_hostfile2, 931 options.system_hostfile2) == 0) 932 return 0; 933 } 934 return check_host_key(host, hostaddr, options.port, host_key, 935 RDRW, options.user_hostfile, options.system_hostfile); 936} 937 938/* 939 * Starts a dialog with the server, and authenticates the current user on the 940 * server. This does not need any extra privileges. The basic connection 941 * to the server must already have been established before this is called. 942 * If login fails, this function prints an error and never returns. 943 * This function does not require super-user privileges. 944 */ 945void 946ssh_login(Sensitive *sensitive, const char *orighost, 947 struct sockaddr *hostaddr, struct passwd *pw) 948{ 949 char *host, *cp; 950 char *server_user, *local_user; 951 952 local_user = xstrdup(pw->pw_name); 953 server_user = options.user ? options.user : local_user; 954 955 /* Convert the user-supplied hostname into all lowercase. */ 956 host = xstrdup(orighost); 957 for (cp = host; *cp; cp++) 958 if (isupper(*cp)) 959 *cp = (char)tolower(*cp); 960 961 /* Exchange protocol version identification strings with the server. */ 962 ssh_exchange_identification(); 963 964 /* Put the connection into non-blocking mode. */ 965 packet_set_nonblocking(); 966 967 /* key exchange */ 968 /* authenticate user */ 969 if (compat20) { 970 ssh_kex2(host, hostaddr); 971 ssh_userauth2(local_user, server_user, host, sensitive); 972 } else { 973 ssh_kex(host, hostaddr); 974 ssh_userauth1(local_user, server_user, host, sensitive); 975 } 976 xfree(local_user); 977} 978 979void 980ssh_put_password(char *password) 981{ 982 int size; 983 char *padded; 984 985 if (datafellows & SSH_BUG_PASSWORDPAD) { 986 packet_put_cstring(password); 987 return; 988 } 989 size = roundup(strlen(password) + 1, 32); 990 padded = xcalloc(1, size); 991 strlcpy(padded, password, size); 992 packet_put_string(padded, size); 993 memset(padded, 0, size); 994 xfree(padded); 995} 996 997static int 998show_key_from_file(const char *file, const char *host, int keytype) 999{ 1000 Key *found; 1001 char *fp; 1002 int line, ret; 1003 1004 found = key_new(keytype); 1005 if ((ret = lookup_key_in_hostfile_by_type(file, host, 1006 keytype, found, &line))) { 1007 fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); 1008 logit("WARNING: %s key found for host %s\n" 1009 "in %s:%d\n" 1010 "%s key fingerprint %s.", 1011 key_type(found), host, file, line, 1012 key_type(found), fp); 1013 xfree(fp); 1014 } 1015 key_free(found); 1016 return (ret); 1017} 1018 1019/* print all known host keys for a given host, but skip keys of given type */ 1020static int 1021show_other_keys(const char *host, Key *key) 1022{ 1023 int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, -1}; 1024 int i, found = 0; 1025 1026 for (i = 0; type[i] != -1; i++) { 1027 if (type[i] == key->type) 1028 continue; 1029 if (type[i] != KEY_RSA1 && 1030 show_key_from_file(options.user_hostfile2, host, type[i])) { 1031 found = 1; 1032 continue; 1033 } 1034 if (type[i] != KEY_RSA1 && 1035 show_key_from_file(options.system_hostfile2, host, type[i])) { 1036 found = 1; 1037 continue; 1038 } 1039 if (show_key_from_file(options.user_hostfile, host, type[i])) { 1040 found = 1; 1041 continue; 1042 } 1043 if (show_key_from_file(options.system_hostfile, host, type[i])) { 1044 found = 1; 1045 continue; 1046 } 1047 debug2("no key of type %d for host %s", type[i], host); 1048 } 1049 return (found); 1050} 1051 1052static void 1053warn_changed_key(Key *host_key) 1054{ 1055 char *fp; 1056 const char *type = key_type(host_key); 1057 1058 fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 1059 1060 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1061 error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 1062 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1063 error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 1064 error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 1065 error("It is also possible that the %s host key has just been changed.", type); 1066 error("The fingerprint for the %s key sent by the remote host is\n%s.", 1067 type, fp); 1068 error("Please contact your system administrator."); 1069 1070 xfree(fp); 1071} 1072 1073/* 1074 * Execute a local command 1075 */ 1076int 1077ssh_local_cmd(const char *args) 1078{ 1079 char *shell; 1080 pid_t pid; 1081 int status; 1082 1083 if (!options.permit_local_command || 1084 args == NULL || !*args) 1085 return (1); 1086 1087 if ((shell = getenv("SHELL")) == NULL) 1088 shell = _PATH_BSHELL; 1089 1090 pid = fork(); 1091 if (pid == 0) { 1092 debug3("Executing %s -c \"%s\"", shell, args); 1093 execl(shell, shell, "-c", args, (char *)NULL); 1094 error("Couldn't execute %s -c \"%s\": %s", 1095 shell, args, strerror(errno)); 1096 _exit(1); 1097 } else if (pid == -1) 1098 fatal("fork failed: %.100s", strerror(errno)); 1099 while (waitpid(pid, &status, 0) == -1) 1100 if (errno != EINTR) 1101 fatal("Couldn't wait for child: %s", strerror(errno)); 1102 1103 if (!WIFEXITED(status)) 1104 return (1); 1105 1106 return (WEXITSTATUS(status)); 1107} 1108