sshconnect.c revision 106130
138494Sobrien/* 2174294Sobrien * Author: Tatu Ylonen <ylo@cs.hut.fi> 338494Sobrien * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 438494Sobrien * All rights reserved 538494Sobrien * Code to connect to a remote host, and to perform the client side of the 638494Sobrien * login (authentication) dialog. 738494Sobrien * 838494Sobrien * As far as I am concerned, the code I have written for this software 938494Sobrien * can be used freely for any purpose. Any derived versions of this 1038494Sobrien * software must be clearly marked as such, and if the derived work is 1138494Sobrien * incompatible with the protocol description in the RFC file, it must be 1238494Sobrien * called by a name other than "ssh" or "Secure Shell". 1338494Sobrien */ 1438494Sobrien 1538494Sobrien#include "includes.h" 1638494SobrienRCSID("$OpenBSD: sshconnect.c,v 1.135 2002/09/19 01:58:18 djm Exp $"); 1738494SobrienRCSID("$FreeBSD: head/crypto/openssh/sshconnect.c 106130 2002-10-29 10:16:02Z des $"); 1838494Sobrien 1938494Sobrien#include <openssl/bn.h> 2042629Sobrien 2138494Sobrien#include "ssh.h" 2238494Sobrien#include "xmalloc.h" 2338494Sobrien#include "rsa.h" 2438494Sobrien#include "buffer.h" 2538494Sobrien#include "packet.h" 2638494Sobrien#include "uidswap.h" 2738494Sobrien#include "compat.h" 2838494Sobrien#include "key.h" 2938494Sobrien#include "sshconnect.h" 3038494Sobrien#include "hostfile.h" 3138494Sobrien#include "log.h" 3238494Sobrien#include "readconf.h" 3338494Sobrien#include "atomicio.h" 3438494Sobrien#include "misc.h" 3538494Sobrien#include "readpass.h" 3638494Sobrien 3738494Sobrienchar *client_version_string = NULL; 3838494Sobrienchar *server_version_string = NULL; 3938494Sobrien 40174294Sobrien/* import */ 4138494Sobrienextern Options options; 4238494Sobrienextern char *__progname; 4338494Sobrienextern uid_t original_real_uid; 4438494Sobrienextern uid_t original_effective_uid; 4538494Sobrienextern pid_t proxy_command_pid; 4638494Sobrien 4738494Sobrien#ifndef INET6_ADDRSTRLEN /* for non IPv6 machines */ 4838494Sobrien#define INET6_ADDRSTRLEN 46 4938494Sobrien#endif 5038494Sobrien 5138494Sobrienstatic int show_other_keys(const char *, Key *); 5238494Sobrien 5338494Sobrien/* 54174294Sobrien * Connect to the given ssh server using a proxy command. 55174294Sobrien */ 5638494Sobrienstatic int 5738494Sobrienssh_proxy_connect(const char *host, u_short port, const char *proxy_command) 5838494Sobrien{ 5938494Sobrien Buffer command; 6038494Sobrien const char *cp; 6138494Sobrien char *command_string; 6238494Sobrien int pin[2], pout[2]; 6338494Sobrien pid_t pid; 6438494Sobrien char strport[NI_MAXSERV]; 6538494Sobrien 6638494Sobrien /* Convert the port number into a string. */ 6738494Sobrien snprintf(strport, sizeof strport, "%hu", port); 6838494Sobrien 6938494Sobrien /* 7038494Sobrien * Build the final command string in the buffer by making the 7138494Sobrien * appropriate substitutions to the given proxy command. 7238494Sobrien * 7338494Sobrien * Use "exec" to avoid "sh -c" processes on some platforms 7438494Sobrien * (e.g. Solaris) 7538494Sobrien */ 7638494Sobrien buffer_init(&command); 7738494Sobrien buffer_append(&command, "exec ", 5); 7838494Sobrien 7938494Sobrien for (cp = proxy_command; *cp; cp++) { 8038494Sobrien if (cp[0] == '%' && cp[1] == '%') { 8138494Sobrien buffer_append(&command, "%", 1); 8238494Sobrien cp++; 8351292Sobrien continue; 8451292Sobrien } 85119679Smbr if (cp[0] == '%' && cp[1] == 'h') { 8638494Sobrien buffer_append(&command, host, strlen(host)); 8751292Sobrien cp++; 8851292Sobrien continue; 8938494Sobrien } 9038494Sobrien if (cp[0] == '%' && cp[1] == 'p') { 9138494Sobrien buffer_append(&command, strport, strlen(strport)); 9238494Sobrien cp++; 9338494Sobrien continue; 94174294Sobrien } 95174294Sobrien buffer_append(&command, cp, 1); 96174294Sobrien } 9738494Sobrien buffer_append(&command, "\0", 1); 9838494Sobrien 9938494Sobrien /* Get the final command string. */ 10038494Sobrien command_string = buffer_ptr(&command); 10138494Sobrien 10238494Sobrien /* Create pipes for communicating with the proxy. */ 10338494Sobrien if (pipe(pin) < 0 || pipe(pout) < 0) 10438494Sobrien fatal("Could not create pipes to communicate with the proxy: %.100s", 10538494Sobrien strerror(errno)); 10638494Sobrien 10738494Sobrien debug("Executing proxy command: %.500s", command_string); 10838494Sobrien 10938494Sobrien /* Fork and execute the proxy command. */ 11038494Sobrien if ((pid = fork()) == 0) { 11138494Sobrien char *argv[10]; 11238494Sobrien 11338494Sobrien /* Child. Permanently give up superuser privileges. */ 11438494Sobrien seteuid(original_real_uid); 11538494Sobrien setuid(original_real_uid); 11638494Sobrien 11738494Sobrien /* Redirect stdin and stdout. */ 11838494Sobrien close(pin[1]); 11938494Sobrien if (pin[0] != 0) { 12038494Sobrien if (dup2(pin[0], 0) < 0) 12138494Sobrien perror("dup2 stdin"); 12238494Sobrien close(pin[0]); 12338494Sobrien } 12438494Sobrien close(pout[0]); 12538494Sobrien if (dup2(pout[1], 1) < 0) 12638494Sobrien perror("dup2 stdout"); 12738494Sobrien /* Cannot be 1 because pin allocated two descriptors. */ 12838494Sobrien close(pout[1]); 12938494Sobrien 13038494Sobrien /* Stderr is left as it is so that error messages get 13138494Sobrien printed on the user's terminal. */ 13238494Sobrien argv[0] = _PATH_BSHELL; 13338494Sobrien argv[1] = "-c"; 13438494Sobrien argv[2] = command_string; 13538494Sobrien argv[3] = NULL; 13638494Sobrien 13738494Sobrien /* Execute the proxy command. Note that we gave up any 13838494Sobrien extra privileges above. */ 13938494Sobrien execv(argv[0], argv); 14038494Sobrien perror(argv[0]); 14138494Sobrien exit(1); 14238494Sobrien } 14338494Sobrien /* Parent. */ 14438494Sobrien if (pid < 0) 14538494Sobrien fatal("fork failed: %.100s", strerror(errno)); 14638494Sobrien else 14738494Sobrien proxy_command_pid = pid; /* save pid to clean up later */ 14838494Sobrien 14938494Sobrien /* Close child side of the descriptors. */ 15038494Sobrien close(pin[0]); 151174294Sobrien close(pout[1]); 15238494Sobrien 153174294Sobrien /* Free the command name. */ 15438494Sobrien buffer_free(&command); 15538494Sobrien 15638494Sobrien /* Set the connection file descriptors. */ 15738494Sobrien packet_set_connection(pout[0], pin[1]); 15838494Sobrien 15938494Sobrien /* Indicate OK return */ 16038494Sobrien return 0; 16138494Sobrien} 16238494Sobrien 16338494Sobrien/* 16438494Sobrien * Creates a (possibly privileged) socket for use as the ssh connection. 16538494Sobrien */ 16638494Sobrienstatic int 16738494Sobrienssh_create_socket(int privileged, int family) 16838494Sobrien{ 16938494Sobrien int sock, gaierr; 17038494Sobrien struct addrinfo hints, *res; 17138494Sobrien 17238494Sobrien /* 17338494Sobrien * If we are running as root and want to connect to a privileged 17438494Sobrien * port, bind our own socket to a privileged port. 17538494Sobrien */ 17638494Sobrien if (privileged) { 17738494Sobrien int p = IPPORT_RESERVED - 1; 17838494Sobrien PRIV_START; 17938494Sobrien sock = rresvport_af(&p, family); 18038494Sobrien PRIV_END; 18138494Sobrien if (sock < 0) 18238494Sobrien error("rresvport: af=%d %.100s", family, strerror(errno)); 18338494Sobrien else 18438494Sobrien debug("Allocated local port %d.", p); 18538494Sobrien return sock; 18638494Sobrien } 18738494Sobrien sock = socket(family, SOCK_STREAM, 0); 18838494Sobrien if (sock < 0) 18938494Sobrien error("socket: %.100s", strerror(errno)); 19038494Sobrien 19138494Sobrien /* Bind the socket to an alternative local IP address */ 19238494Sobrien if (options.bind_address == NULL) 19338494Sobrien return sock; 194174294Sobrien 19538494Sobrien memset(&hints, 0, sizeof(hints)); 19638494Sobrien hints.ai_family = family; 19738494Sobrien hints.ai_socktype = SOCK_STREAM; 19838494Sobrien hints.ai_flags = AI_PASSIVE; 19938494Sobrien gaierr = getaddrinfo(options.bind_address, "0", &hints, &res); 20038494Sobrien if (gaierr) { 20138494Sobrien error("getaddrinfo: %s: %s", options.bind_address, 20238494Sobrien gai_strerror(gaierr)); 20338494Sobrien close(sock); 20438494Sobrien return -1; 20538494Sobrien } 20638494Sobrien if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 20738494Sobrien error("bind: %s: %s", options.bind_address, strerror(errno)); 20838494Sobrien close(sock); 209174294Sobrien freeaddrinfo(res); 21038494Sobrien return -1; 21138494Sobrien } 21238494Sobrien freeaddrinfo(res); 213174294Sobrien return sock; 21438494Sobrien} 215174294Sobrien 216174294Sobrien/* 217174294Sobrien * Opens a TCP/IP connection to the remote server on the given host. 218174294Sobrien * The address of the remote host will be returned in hostaddr. 21938494Sobrien * If port is 0, the default port will be used. If needpriv is true, 22038494Sobrien * a privileged port will be allocated to make the connection. 22138494Sobrien * This requires super-user privileges if needpriv is true. 22238494Sobrien * Connection_attempts specifies the maximum number of tries (one per 22338494Sobrien * second). If proxy_command is non-NULL, it specifies the command (with %h 22438494Sobrien * and %p substituted for host and port, respectively) to use to contact 22538494Sobrien * the daemon. 22638494Sobrien * Return values: 227174294Sobrien * 0 for OK 22838494Sobrien * ECONNREFUSED if we got a "Connection Refused" by the peer on any address 22938494Sobrien * ECONNABORTED if we failed without a "Connection refused" 23038494Sobrien * Suitable error messages for the connection failure will already have been 23138494Sobrien * printed. 23238494Sobrien */ 23338494Sobrienint 23438494Sobrienssh_connect(const char *host, struct sockaddr_storage * hostaddr, 23538494Sobrien u_short port, int family, int connection_attempts, 23638494Sobrien int needpriv, const char *proxy_command) 23738494Sobrien{ 238174294Sobrien int gaierr; 23938494Sobrien int on = 1; 24038494Sobrien int sock = -1, attempt; 24138494Sobrien char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 24238494Sobrien struct addrinfo hints, *ai, *aitop; 243174294Sobrien struct servent *sp; 244174294Sobrien /* 245174294Sobrien * Did we get only other errors than "Connection refused" (which 246174294Sobrien * should block fallback to rsh and similar), or did we get at least 247174294Sobrien * one "Connection refused"? 24838494Sobrien */ 24938494Sobrien int full_failure = 1; 25038494Sobrien 25151292Sobrien debug("ssh_connect: needpriv %d", needpriv); 25238494Sobrien 25338494Sobrien /* Get default port if port has not been set. */ 25438494Sobrien if (port == 0) { 25538494Sobrien sp = getservbyname(SSH_SERVICE_NAME, "tcp"); 25638494Sobrien if (sp) 25738494Sobrien port = ntohs(sp->s_port); 25838494Sobrien else 25938494Sobrien port = SSH_DEFAULT_PORT; 26038494Sobrien } 26138494Sobrien /* If a proxy command is given, connect using it. */ 26238494Sobrien if (proxy_command != NULL) 26338494Sobrien return ssh_proxy_connect(host, port, proxy_command); 264119679Smbr 26538494Sobrien /* No proxy command. */ 26638494Sobrien 26738494Sobrien memset(&hints, 0, sizeof(hints)); 26838494Sobrien hints.ai_family = family; 26938494Sobrien hints.ai_socktype = SOCK_STREAM; 27038494Sobrien snprintf(strport, sizeof strport, "%u", port); 27138494Sobrien if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 272174294Sobrien fatal("%s: %.100s: %s", __progname, host, 27338494Sobrien gai_strerror(gaierr)); 27438494Sobrien 27538494Sobrien /* 27638494Sobrien * Try to connect several times. On some machines, the first time 27738494Sobrien * will sometimes fail. In general socket code appears to behave 27838494Sobrien * quite magically on many machines. 27938494Sobrien */ 28038494Sobrien for (attempt = 0; ;) { 28138494Sobrien if (attempt > 0) 28238494Sobrien debug("Trying again..."); 283174294Sobrien 284174294Sobrien /* Loop through addresses for this host, and try each one in 285174294Sobrien sequence until the connection succeeds. */ 286174294Sobrien for (ai = aitop; ai; ai = ai->ai_next) { 287174294Sobrien if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 28838494Sobrien continue; 28938494Sobrien if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 29038494Sobrien ntop, sizeof(ntop), strport, sizeof(strport), 29138494Sobrien NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 29238494Sobrien error("ssh_connect: getnameinfo failed"); 29338494Sobrien continue; 29438494Sobrien } 29538494Sobrien debug("Connecting to %.200s [%.100s] port %s.", 296174294Sobrien host, ntop, strport); 29738494Sobrien 298174294Sobrien /* Create a socket for connecting. */ 29938494Sobrien sock = ssh_create_socket(needpriv, ai->ai_family); 300174294Sobrien if (sock < 0) 30138494Sobrien /* Any error is already output */ 30238494Sobrien continue; 30338494Sobrien 30438494Sobrien if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { 30538494Sobrien /* Successful connection. */ 30638494Sobrien memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 30738494Sobrien break; 30838494Sobrien } else { 30938494Sobrien if (errno == ECONNREFUSED) 31038494Sobrien full_failure = 0; 31138494Sobrien debug("connect to address %s port %s: %s", 31238494Sobrien ntop, strport, strerror(errno)); 31338494Sobrien /* 31438494Sobrien * Close the failed socket; there appear to 31538494Sobrien * be some problems when reusing a socket for 31638494Sobrien * which connect() has already returned an 31738494Sobrien * error. 31838494Sobrien */ 31938494Sobrien close(sock); 32038494Sobrien } 32138494Sobrien } 32238494Sobrien if (ai) 32338494Sobrien break; /* Successful connection. */ 32438494Sobrien 32538494Sobrien attempt++; 32638494Sobrien if (attempt >= connection_attempts) 32738494Sobrien break; 32838494Sobrien /* Sleep a moment before retrying. */ 32938494Sobrien sleep(1); 33038494Sobrien } 33138494Sobrien 33238494Sobrien freeaddrinfo(aitop); 33338494Sobrien 33438494Sobrien /* Return failure if we didn't get a successful connection. */ 335174294Sobrien if (attempt >= connection_attempts) { 336174294Sobrien log("ssh: connect to host %s port %s: %s", 337174294Sobrien host, strport, strerror(errno)); 338174294Sobrien return full_failure ? ECONNABORTED : ECONNREFUSED; 33938494Sobrien } 340174294Sobrien 341174294Sobrien debug("Connection established."); 34238494Sobrien 34338494Sobrien /* Set keepalives if requested. */ 34438494Sobrien if (options.keepalives && 34538494Sobrien setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 34638494Sobrien sizeof(on)) < 0) 34738494Sobrien error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 34838494Sobrien 34938494Sobrien /* Set the connection. */ 35038494Sobrien packet_set_connection(sock, sock); 35138494Sobrien 35238494Sobrien return 0; 35338494Sobrien} 35438494Sobrien 35538494Sobrien/* 35638494Sobrien * Waits for the server identification string, and sends our own 35738494Sobrien * identification string. 35838494Sobrien */ 35938494Sobrienstatic void 36038494Sobrienssh_exchange_identification(void) 36138494Sobrien{ 36238494Sobrien char buf[256], remote_version[256]; /* must be same size! */ 363174294Sobrien int remote_major, remote_minor, i, mismatch; 364174294Sobrien int connection_in = packet_get_connection_in(); 365174294Sobrien int connection_out = packet_get_connection_out(); 366174294Sobrien int minor1 = PROTOCOL_MINOR_1; 367174294Sobrien 368174294Sobrien /* Read other side\'s version identification. */ 369174294Sobrien for (;;) { 370174294Sobrien for (i = 0; i < sizeof(buf) - 1; i++) { 371174294Sobrien int len = atomicio(read, connection_in, &buf[i], 1); 372174294Sobrien if (len < 0) 373174294Sobrien fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); 374174294Sobrien if (len != 1) 375174294Sobrien fatal("ssh_exchange_identification: Connection closed by remote host"); 376174294Sobrien if (buf[i] == '\r') { 377174294Sobrien buf[i] = '\n'; 378174294Sobrien buf[i + 1] = 0; 379174294Sobrien continue; /**XXX wait for \n */ 380174294Sobrien } 381174294Sobrien if (buf[i] == '\n') { 382174294Sobrien buf[i + 1] = 0; 383174294Sobrien break; 384174294Sobrien } 385174294Sobrien } 386174294Sobrien buf[sizeof(buf) - 1] = 0; 387174294Sobrien if (strncmp(buf, "SSH-", 4) == 0) 388174294Sobrien break; 389174294Sobrien debug("ssh_exchange_identification: %s", buf); 390174294Sobrien } 391174294Sobrien server_version_string = xstrdup(buf); 392174294Sobrien 393174294Sobrien /* 394174294Sobrien * Check that the versions match. In future this might accept 395174294Sobrien * several versions and set appropriate flags to handle them. 396174294Sobrien */ 397174294Sobrien if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 398174294Sobrien &remote_major, &remote_minor, remote_version) != 3) 399174294Sobrien fatal("Bad remote protocol version identification: '%.100s'", buf); 400174294Sobrien debug("Remote protocol version %d.%d, remote software version %.100s", 401174294Sobrien remote_major, remote_minor, remote_version); 402174294Sobrien 40338494Sobrien compat_datafellows(remote_version); 40438494Sobrien mismatch = 0; 40538494Sobrien 40638494Sobrien switch (remote_major) { 40738494Sobrien case 1: 40838494Sobrien if (remote_minor == 99 && 40938494Sobrien (options.protocol & SSH_PROTO_2) && 41038494Sobrien !(options.protocol & SSH_PROTO_1_PREFERRED)) { 41138494Sobrien enable_compat20(); 41238494Sobrien break; 41338494Sobrien } 41438494Sobrien if (!(options.protocol & SSH_PROTO_1)) { 41538494Sobrien mismatch = 1; 41638494Sobrien break; 41738494Sobrien } 41838494Sobrien if (remote_minor < 3) { 419174294Sobrien fatal("Remote machine has too old SSH software version."); 42038494Sobrien } else if (remote_minor == 3 || remote_minor == 4) { 42138494Sobrien /* We speak 1.3, too. */ 42238494Sobrien enable_compat13(); 42338494Sobrien minor1 = 3; 42438494Sobrien if (options.forward_agent) { 425174294Sobrien log("Agent forwarding disabled for protocol 1.3"); 426174294Sobrien options.forward_agent = 0; 42738494Sobrien } 42838494Sobrien } 42938494Sobrien break; 43038494Sobrien case 2: 43138494Sobrien if (options.protocol & SSH_PROTO_2) { 432174294Sobrien enable_compat20(); 433174294Sobrien break; 43438494Sobrien } 43538494Sobrien /* FALLTHROUGH */ 43638494Sobrien default: 43738494Sobrien mismatch = 1; 43838494Sobrien break; 43938494Sobrien } 44038494Sobrien if (mismatch) 44138494Sobrien fatal("Protocol major versions differ: %d vs. %d", 44238494Sobrien (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 44338494Sobrien remote_major); 44438494Sobrien /* Send our own protocol version identification. */ 44538494Sobrien snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 44638494Sobrien compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 44738494Sobrien compat20 ? PROTOCOL_MINOR_2 : minor1, 44838494Sobrien SSH_VERSION); 44938494Sobrien if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) 45038494Sobrien fatal("write: %.100s", strerror(errno)); 45138494Sobrien client_version_string = xstrdup(buf); 45238494Sobrien chop(client_version_string); 45338494Sobrien chop(server_version_string); 454 debug("Local version string %.100s", client_version_string); 455} 456 457/* defaults to 'no' */ 458static int 459confirm(const char *prompt) 460{ 461 const char *msg, *again = "Please type 'yes' or 'no': "; 462 char *p; 463 int ret = -1; 464 465 if (options.batch_mode) 466 return 0; 467 for (msg = prompt;;msg = again) { 468 p = read_passphrase(msg, RP_ECHO); 469 if (p == NULL || 470 (p[0] == '\0') || (p[0] == '\n') || 471 strncasecmp(p, "no", 2) == 0) 472 ret = 0; 473 if (p && strncasecmp(p, "yes", 3) == 0) 474 ret = 1; 475 if (p) 476 xfree(p); 477 if (ret != -1) 478 return ret; 479 } 480} 481 482/* 483 * check whether the supplied host key is valid, return -1 if the key 484 * is not valid. the user_hostfile will not be updated if 'readonly' is true. 485 */ 486static int 487check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, 488 int readonly, const char *user_hostfile, const char *system_hostfile) 489{ 490 Key *file_key; 491 char *type = key_type(host_key); 492 char *ip = NULL; 493 char hostline[1000], *hostp, *fp; 494 HostStatus host_status; 495 HostStatus ip_status; 496 int local = 0, host_ip_differ = 0; 497 int salen; 498 char ntop[NI_MAXHOST]; 499 char msg[1024]; 500 int len, host_line, ip_line, has_keys; 501 const char *host_file = NULL, *ip_file = NULL; 502 503 /* 504 * Force accepting of the host key for loopback/localhost. The 505 * problem is that if the home directory is NFS-mounted to multiple 506 * machines, localhost will refer to a different machine in each of 507 * them, and the user will get bogus HOST_CHANGED warnings. This 508 * essentially disables host authentication for localhost; however, 509 * this is probably not a real problem. 510 */ 511 /** hostaddr == 0! */ 512 switch (hostaddr->sa_family) { 513 case AF_INET: 514 local = (ntohl(((struct sockaddr_in *)hostaddr)-> 515 sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 516 salen = sizeof(struct sockaddr_in); 517 break; 518 case AF_INET6: 519 local = IN6_IS_ADDR_LOOPBACK( 520 &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 521 salen = sizeof(struct sockaddr_in6); 522 break; 523 default: 524 local = 0; 525 salen = sizeof(struct sockaddr_storage); 526 break; 527 } 528 if (options.no_host_authentication_for_localhost == 1 && local && 529 options.host_key_alias == NULL) { 530 debug("Forcing accepting of host key for " 531 "loopback/localhost."); 532 return 0; 533 } 534 535 /* 536 * We don't have the remote ip-address for connections 537 * using a proxy command 538 */ 539 if (options.proxy_command == NULL) { 540 if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop), 541 NULL, 0, NI_NUMERICHOST) != 0) 542 fatal("check_host_key: getnameinfo failed"); 543 ip = xstrdup(ntop); 544 } else { 545 ip = xstrdup("<no hostip for proxy command>"); 546 } 547 /* 548 * Turn off check_host_ip if the connection is to localhost, via proxy 549 * command or if we don't have a hostname to compare with 550 */ 551 if (options.check_host_ip && 552 (local || strcmp(host, ip) == 0 || options.proxy_command != NULL)) 553 options.check_host_ip = 0; 554 555 /* 556 * Allow the user to record the key under a different name. This is 557 * useful for ssh tunneling over forwarded connections or if you run 558 * multiple sshd's on different ports on the same machine. 559 */ 560 if (options.host_key_alias != NULL) { 561 host = options.host_key_alias; 562 debug("using hostkeyalias: %s", host); 563 } 564 565 /* 566 * Store the host key from the known host file in here so that we can 567 * compare it with the key for the IP address. 568 */ 569 file_key = key_new(host_key->type); 570 571 /* 572 * Check if the host key is present in the user\'s list of known 573 * hosts or in the systemwide list. 574 */ 575 host_file = user_hostfile; 576 host_status = check_host_in_hostfile(host_file, host, host_key, 577 file_key, &host_line); 578 if (host_status == HOST_NEW) { 579 host_file = system_hostfile; 580 host_status = check_host_in_hostfile(host_file, host, host_key, 581 file_key, &host_line); 582 } 583 /* 584 * Also perform check for the ip address, skip the check if we are 585 * localhost or the hostname was an ip address to begin with 586 */ 587 if (options.check_host_ip) { 588 Key *ip_key = key_new(host_key->type); 589 590 ip_file = user_hostfile; 591 ip_status = check_host_in_hostfile(ip_file, ip, host_key, 592 ip_key, &ip_line); 593 if (ip_status == HOST_NEW) { 594 ip_file = system_hostfile; 595 ip_status = check_host_in_hostfile(ip_file, ip, 596 host_key, ip_key, &ip_line); 597 } 598 if (host_status == HOST_CHANGED && 599 (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) 600 host_ip_differ = 1; 601 602 key_free(ip_key); 603 } else 604 ip_status = host_status; 605 606 key_free(file_key); 607 608 switch (host_status) { 609 case HOST_OK: 610 /* The host is known and the key matches. */ 611 debug("Host '%.200s' is known and matches the %s host key.", 612 host, type); 613 debug("Found key in %s:%d", host_file, host_line); 614 if (options.check_host_ip && ip_status == HOST_NEW) { 615 if (readonly) 616 log("%s host key for IP address " 617 "'%.128s' not in list of known hosts.", 618 type, ip); 619 else if (!add_host_to_hostfile(user_hostfile, ip, 620 host_key)) 621 log("Failed to add the %s host key for IP " 622 "address '%.128s' to the list of known " 623 "hosts (%.30s).", type, ip, user_hostfile); 624 else 625 log("Warning: Permanently added the %s host " 626 "key for IP address '%.128s' to the list " 627 "of known hosts.", type, ip); 628 } 629 break; 630 case HOST_NEW: 631 if (readonly) 632 goto fail; 633 /* The host is new. */ 634 if (options.strict_host_key_checking == 1) { 635 /* 636 * User has requested strict host key checking. We 637 * will not add the host key automatically. The only 638 * alternative left is to abort. 639 */ 640 error("No %s host key is known for %.200s and you " 641 "have requested strict checking.", type, host); 642 goto fail; 643 } else if (options.strict_host_key_checking == 2) { 644 has_keys = show_other_keys(host, host_key); 645 /* The default */ 646 fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 647 snprintf(msg, sizeof(msg), 648 "The authenticity of host '%.200s (%s)' can't be " 649 "established%s\n" 650 "%s key fingerprint is %s.\n" 651 "Are you sure you want to continue connecting " 652 "(yes/no)? ", 653 host, ip, 654 has_keys ? ",\nbut keys of different type are already " 655 "known for this host." : ".", 656 type, fp); 657 xfree(fp); 658 if (!confirm(msg)) 659 goto fail; 660 } 661 if (options.check_host_ip && ip_status == HOST_NEW) { 662 snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 663 hostp = hostline; 664 } else 665 hostp = host; 666 667 /* 668 * If not in strict mode, add the key automatically to the 669 * local known_hosts file. 670 */ 671 if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) 672 log("Failed to add the host to the list of known " 673 "hosts (%.500s).", user_hostfile); 674 else 675 log("Warning: Permanently added '%.200s' (%s) to the " 676 "list of known hosts.", hostp, type); 677 break; 678 case HOST_CHANGED: 679 if (options.check_host_ip && host_ip_differ) { 680 char *msg; 681 if (ip_status == HOST_NEW) 682 msg = "is unknown"; 683 else if (ip_status == HOST_OK) 684 msg = "is unchanged"; 685 else 686 msg = "has a different value"; 687 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 688 error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 689 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 690 error("The %s host key for %s has changed,", type, host); 691 error("and the key for the according IP address %s", ip); 692 error("%s. This could either mean that", msg); 693 error("DNS SPOOFING is happening or the IP address for the host"); 694 error("and its host key have changed at the same time."); 695 if (ip_status != HOST_NEW) 696 error("Offending key for IP in %s:%d", ip_file, ip_line); 697 } 698 /* The host key has changed. */ 699 fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 700 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 701 error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 702 error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 703 error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 704 error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 705 error("It is also possible that the %s host key has just been changed.", type); 706 error("The fingerprint for the %s key sent by the remote host is\n%s.", 707 type, fp); 708 error("Please contact your system administrator."); 709 error("Add correct host key in %.100s to get rid of this message.", 710 user_hostfile); 711 error("Offending key in %s:%d", host_file, host_line); 712 xfree(fp); 713 714 /* 715 * If strict host key checking is in use, the user will have 716 * to edit the key manually and we can only abort. 717 */ 718 if (options.strict_host_key_checking) { 719 error("%s host key for %.200s has changed and you have " 720 "requested strict checking.", type, host); 721 goto fail; 722 } 723 724 /* 725 * If strict host key checking has not been requested, allow 726 * the connection but without password authentication or 727 * agent forwarding. 728 */ 729 if (options.password_authentication) { 730 error("Password authentication is disabled to avoid " 731 "man-in-the-middle attacks."); 732 options.password_authentication = 0; 733 } 734 if (options.forward_agent) { 735 error("Agent forwarding is disabled to avoid " 736 "man-in-the-middle attacks."); 737 options.forward_agent = 0; 738 } 739 if (options.forward_x11) { 740 error("X11 forwarding is disabled to avoid " 741 "man-in-the-middle attacks."); 742 options.forward_x11 = 0; 743 } 744 if (options.num_local_forwards > 0 || 745 options.num_remote_forwards > 0) { 746 error("Port forwarding is disabled to avoid " 747 "man-in-the-middle attacks."); 748 options.num_local_forwards = 749 options.num_remote_forwards = 0; 750 } 751 /* 752 * XXX Should permit the user to change to use the new id. 753 * This could be done by converting the host key to an 754 * identifying sentence, tell that the host identifies itself 755 * by that sentence, and ask the user if he/she whishes to 756 * accept the authentication. 757 */ 758 break; 759 case HOST_FOUND: 760 fatal("internal error"); 761 break; 762 } 763 764 if (options.check_host_ip && host_status != HOST_CHANGED && 765 ip_status == HOST_CHANGED) { 766 snprintf(msg, sizeof(msg), 767 "Warning: the %s host key for '%.200s' " 768 "differs from the key for the IP address '%.128s'" 769 "\nOffending key for IP in %s:%d", 770 type, host, ip, ip_file, ip_line); 771 if (host_status == HOST_OK) { 772 len = strlen(msg); 773 snprintf(msg + len, sizeof(msg) - len, 774 "\nMatching host key in %s:%d", 775 host_file, host_line); 776 } 777 if (options.strict_host_key_checking == 1) { 778 log(msg); 779 error("Exiting, you have requested strict checking."); 780 goto fail; 781 } else if (options.strict_host_key_checking == 2) { 782 strlcat(msg, "\nAre you sure you want " 783 "to continue connecting (yes/no)? ", sizeof(msg)); 784 if (!confirm(msg)) 785 goto fail; 786 } else { 787 log(msg); 788 } 789 } 790 791 xfree(ip); 792 return 0; 793 794fail: 795 xfree(ip); 796 return -1; 797} 798 799int 800verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 801{ 802 struct stat st; 803 804 /* return ok if the key can be found in an old keyfile */ 805 if (stat(options.system_hostfile2, &st) == 0 || 806 stat(options.user_hostfile2, &st) == 0) { 807 if (check_host_key(host, hostaddr, host_key, /*readonly*/ 1, 808 options.user_hostfile2, options.system_hostfile2) == 0) 809 return 0; 810 } 811 return check_host_key(host, hostaddr, host_key, /*readonly*/ 0, 812 options.user_hostfile, options.system_hostfile); 813} 814 815/* 816 * Starts a dialog with the server, and authenticates the current user on the 817 * server. This does not need any extra privileges. The basic connection 818 * to the server must already have been established before this is called. 819 * If login fails, this function prints an error and never returns. 820 * This function does not require super-user privileges. 821 */ 822void 823ssh_login(Sensitive *sensitive, const char *orighost, 824 struct sockaddr *hostaddr, struct passwd *pw) 825{ 826 char *host, *cp; 827 char *server_user, *local_user; 828 829 local_user = xstrdup(pw->pw_name); 830 server_user = options.user ? options.user : local_user; 831 832 /* Convert the user-supplied hostname into all lowercase. */ 833 host = xstrdup(orighost); 834 for (cp = host; *cp; cp++) 835 if (isupper(*cp)) 836 *cp = tolower(*cp); 837 838 /* Exchange protocol version identification strings with the server. */ 839 ssh_exchange_identification(); 840 841 /* Put the connection into non-blocking mode. */ 842 packet_set_nonblocking(); 843 844 /* key exchange */ 845 /* authenticate user */ 846 if (compat20) { 847 ssh_kex2(host, hostaddr); 848 ssh_userauth2(local_user, server_user, host, sensitive); 849 } else { 850 ssh_kex(host, hostaddr); 851 ssh_userauth1(local_user, server_user, host, sensitive); 852 } 853} 854 855void 856ssh_put_password(char *password) 857{ 858 int size; 859 char *padded; 860 861 if (datafellows & SSH_BUG_PASSWORDPAD) { 862 packet_put_cstring(password); 863 return; 864 } 865 size = roundup(strlen(password) + 1, 32); 866 padded = xmalloc(size); 867 memset(padded, 0, size); 868 strlcpy(padded, password, size); 869 packet_put_string(padded, size); 870 memset(padded, 0, size); 871 xfree(padded); 872} 873 874static int 875show_key_from_file(const char *file, const char *host, int keytype) 876{ 877 Key *found; 878 char *fp; 879 int line, ret; 880 881 found = key_new(keytype); 882 if ((ret = lookup_key_in_hostfile_by_type(file, host, 883 keytype, found, &line))) { 884 fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); 885 log("WARNING: %s key found for host %s\n" 886 "in %s:%d\n" 887 "%s key fingerprint %s.", 888 key_type(found), host, file, line, 889 key_type(found), fp); 890 xfree(fp); 891 } 892 key_free(found); 893 return (ret); 894} 895 896/* print all known host keys for a given host, but skip keys of given type */ 897static int 898show_other_keys(const char *host, Key *key) 899{ 900 int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, -1}; 901 int i, found = 0; 902 903 for (i = 0; type[i] != -1; i++) { 904 if (type[i] == key->type) 905 continue; 906 if (type[i] != KEY_RSA1 && 907 show_key_from_file(options.user_hostfile2, host, type[i])) { 908 found = 1; 909 continue; 910 } 911 if (type[i] != KEY_RSA1 && 912 show_key_from_file(options.system_hostfile2, host, type[i])) { 913 found = 1; 914 continue; 915 } 916 if (show_key_from_file(options.user_hostfile, host, type[i])) { 917 found = 1; 918 continue; 919 } 920 if (show_key_from_file(options.system_hostfile, host, type[i])) { 921 found = 1; 922 continue; 923 } 924 debug2("no key of type %d for host %s", type[i], host); 925 } 926 return (found); 927} 928