1511b41d2SMark Murray /* 2511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 3511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4511b41d2SMark Murray * All rights reserved 5511b41d2SMark Murray * Code to connect to a remote host, and to perform the client side of the 6511b41d2SMark Murray * login (authentication) dialog. 742f71286SMark Murray * 8c2d3a559SKris Kennaway * As far as I am concerned, the code I have written for this software 9c2d3a559SKris Kennaway * can be used freely for any purpose. Any derived versions of this 10c2d3a559SKris Kennaway * software must be clearly marked as such, and if the derived work is 11c2d3a559SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 12c2d3a559SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 13511b41d2SMark Murray */ 14511b41d2SMark Murray 15511b41d2SMark Murray #include "includes.h" 16cf2b5f3bSDag-Erling Smørgrav RCSID("$OpenBSD: sshconnect.c,v 1.148 2003/09/18 07:52:54 markus Exp $"); 17511b41d2SMark Murray 1818a71195SBrian Feldman #include <openssl/bn.h> 19e8aafc91SKris Kennaway 20ca3176e7SBrian Feldman #include "ssh.h" 21511b41d2SMark Murray #include "xmalloc.h" 22511b41d2SMark Murray #include "rsa.h" 23e8aafc91SKris Kennaway #include "buffer.h" 24511b41d2SMark Murray #include "packet.h" 25511b41d2SMark Murray #include "uidswap.h" 26511b41d2SMark Murray #include "compat.h" 273c6ae118SKris Kennaway #include "key.h" 28e8aafc91SKris Kennaway #include "sshconnect.h" 293c6ae118SKris Kennaway #include "hostfile.h" 30ca3176e7SBrian Feldman #include "log.h" 31ca3176e7SBrian Feldman #include "readconf.h" 32ca3176e7SBrian Feldman #include "atomicio.h" 33ca3176e7SBrian Feldman #include "misc.h" 34af12a3e7SDag-Erling Smørgrav #include "readpass.h" 35511b41d2SMark Murray 36cf2b5f3bSDag-Erling Smørgrav #ifdef DNS 37cf2b5f3bSDag-Erling Smørgrav #include "dns.h" 38cf2b5f3bSDag-Erling Smørgrav #endif 39cf2b5f3bSDag-Erling Smørgrav 40e8aafc91SKris Kennaway char *client_version_string = NULL; 41e8aafc91SKris Kennaway char *server_version_string = NULL; 42511b41d2SMark Murray 43cf2b5f3bSDag-Erling Smørgrav #ifdef DNS 44cf2b5f3bSDag-Erling Smørgrav int verified_host_key_dns = 0; 45cf2b5f3bSDag-Erling Smørgrav #endif 46cf2b5f3bSDag-Erling Smørgrav 4780628bacSDag-Erling Smørgrav /* import */ 48511b41d2SMark Murray extern Options options; 49511b41d2SMark Murray extern char *__progname; 5080628bacSDag-Erling Smørgrav extern uid_t original_real_uid; 5180628bacSDag-Erling Smørgrav extern uid_t original_effective_uid; 52f388f5efSDag-Erling Smørgrav extern pid_t proxy_command_pid; 53511b41d2SMark Murray 54989dd127SDag-Erling Smørgrav #ifndef INET6_ADDRSTRLEN /* for non IPv6 machines */ 55989dd127SDag-Erling Smørgrav #define INET6_ADDRSTRLEN 46 56989dd127SDag-Erling Smørgrav #endif 57989dd127SDag-Erling Smørgrav 58f388f5efSDag-Erling Smørgrav static int show_other_keys(const char *, Key *); 59ca3176e7SBrian Feldman 60511b41d2SMark Murray /* 61511b41d2SMark Murray * Connect to the given ssh server using a proxy command. 62511b41d2SMark Murray */ 63af12a3e7SDag-Erling Smørgrav static int 6480628bacSDag-Erling Smørgrav ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) 65511b41d2SMark Murray { 66511b41d2SMark Murray Buffer command; 67511b41d2SMark Murray const char *cp; 68511b41d2SMark Murray char *command_string; 69511b41d2SMark Murray int pin[2], pout[2]; 70e8aafc91SKris Kennaway pid_t pid; 71511b41d2SMark Murray char strport[NI_MAXSERV]; 72511b41d2SMark Murray 73511b41d2SMark Murray /* Convert the port number into a string. */ 74511b41d2SMark Murray snprintf(strport, sizeof strport, "%hu", port); 75511b41d2SMark Murray 76f388f5efSDag-Erling Smørgrav /* 77f388f5efSDag-Erling Smørgrav * Build the final command string in the buffer by making the 78f388f5efSDag-Erling Smørgrav * appropriate substitutions to the given proxy command. 79f388f5efSDag-Erling Smørgrav * 80f388f5efSDag-Erling Smørgrav * Use "exec" to avoid "sh -c" processes on some platforms 81f388f5efSDag-Erling Smørgrav * (e.g. Solaris) 82f388f5efSDag-Erling Smørgrav */ 83511b41d2SMark Murray buffer_init(&command); 84f388f5efSDag-Erling Smørgrav buffer_append(&command, "exec ", 5); 85f388f5efSDag-Erling Smørgrav 86511b41d2SMark Murray for (cp = proxy_command; *cp; cp++) { 87511b41d2SMark Murray if (cp[0] == '%' && cp[1] == '%') { 88511b41d2SMark Murray buffer_append(&command, "%", 1); 89511b41d2SMark Murray cp++; 90511b41d2SMark Murray continue; 91511b41d2SMark Murray } 92511b41d2SMark Murray if (cp[0] == '%' && cp[1] == 'h') { 93511b41d2SMark Murray buffer_append(&command, host, strlen(host)); 94511b41d2SMark Murray cp++; 95511b41d2SMark Murray continue; 96511b41d2SMark Murray } 97511b41d2SMark Murray if (cp[0] == '%' && cp[1] == 'p') { 98511b41d2SMark Murray buffer_append(&command, strport, strlen(strport)); 99511b41d2SMark Murray cp++; 100511b41d2SMark Murray continue; 101511b41d2SMark Murray } 102511b41d2SMark Murray buffer_append(&command, cp, 1); 103511b41d2SMark Murray } 104511b41d2SMark Murray buffer_append(&command, "\0", 1); 105511b41d2SMark Murray 106511b41d2SMark Murray /* Get the final command string. */ 107511b41d2SMark Murray command_string = buffer_ptr(&command); 108511b41d2SMark Murray 109511b41d2SMark Murray /* Create pipes for communicating with the proxy. */ 110511b41d2SMark Murray if (pipe(pin) < 0 || pipe(pout) < 0) 111511b41d2SMark Murray fatal("Could not create pipes to communicate with the proxy: %.100s", 112511b41d2SMark Murray strerror(errno)); 113511b41d2SMark Murray 114511b41d2SMark Murray debug("Executing proxy command: %.500s", command_string); 115511b41d2SMark Murray 116511b41d2SMark Murray /* Fork and execute the proxy command. */ 117511b41d2SMark Murray if ((pid = fork()) == 0) { 118511b41d2SMark Murray char *argv[10]; 119511b41d2SMark Murray 120511b41d2SMark Murray /* Child. Permanently give up superuser privileges. */ 12180628bacSDag-Erling Smørgrav seteuid(original_real_uid); 12280628bacSDag-Erling Smørgrav setuid(original_real_uid); 123511b41d2SMark Murray 124511b41d2SMark Murray /* Redirect stdin and stdout. */ 125511b41d2SMark Murray close(pin[1]); 126511b41d2SMark Murray if (pin[0] != 0) { 127511b41d2SMark Murray if (dup2(pin[0], 0) < 0) 128511b41d2SMark Murray perror("dup2 stdin"); 129511b41d2SMark Murray close(pin[0]); 130511b41d2SMark Murray } 131511b41d2SMark Murray close(pout[0]); 132511b41d2SMark Murray if (dup2(pout[1], 1) < 0) 133511b41d2SMark Murray perror("dup2 stdout"); 134511b41d2SMark Murray /* Cannot be 1 because pin allocated two descriptors. */ 135511b41d2SMark Murray close(pout[1]); 136511b41d2SMark Murray 137511b41d2SMark Murray /* Stderr is left as it is so that error messages get 138511b41d2SMark Murray printed on the user's terminal. */ 139ca3176e7SBrian Feldman argv[0] = _PATH_BSHELL; 140511b41d2SMark Murray argv[1] = "-c"; 141511b41d2SMark Murray argv[2] = command_string; 142511b41d2SMark Murray argv[3] = NULL; 143511b41d2SMark Murray 144511b41d2SMark Murray /* Execute the proxy command. Note that we gave up any 145511b41d2SMark Murray extra privileges above. */ 146ca3176e7SBrian Feldman execv(argv[0], argv); 147ca3176e7SBrian Feldman perror(argv[0]); 148511b41d2SMark Murray exit(1); 149511b41d2SMark Murray } 150511b41d2SMark Murray /* Parent. */ 151511b41d2SMark Murray if (pid < 0) 152511b41d2SMark Murray fatal("fork failed: %.100s", strerror(errno)); 153f388f5efSDag-Erling Smørgrav else 154f388f5efSDag-Erling Smørgrav proxy_command_pid = pid; /* save pid to clean up later */ 155511b41d2SMark Murray 156511b41d2SMark Murray /* Close child side of the descriptors. */ 157511b41d2SMark Murray close(pin[0]); 158511b41d2SMark Murray close(pout[1]); 159511b41d2SMark Murray 160511b41d2SMark Murray /* Free the command name. */ 161511b41d2SMark Murray buffer_free(&command); 162511b41d2SMark Murray 163511b41d2SMark Murray /* Set the connection file descriptors. */ 164511b41d2SMark Murray packet_set_connection(pout[0], pin[1]); 165511b41d2SMark Murray 166af12a3e7SDag-Erling Smørgrav /* Indicate OK return */ 167af12a3e7SDag-Erling Smørgrav return 0; 168511b41d2SMark Murray } 169511b41d2SMark Murray 170511b41d2SMark Murray /* 171511b41d2SMark Murray * Creates a (possibly privileged) socket for use as the ssh connection. 172511b41d2SMark Murray */ 173af12a3e7SDag-Erling Smørgrav static int 174cf2b5f3bSDag-Erling Smørgrav ssh_create_socket(int privileged, struct addrinfo *ai) 175511b41d2SMark Murray { 176af12a3e7SDag-Erling Smørgrav int sock, gaierr; 177af12a3e7SDag-Erling Smørgrav struct addrinfo hints, *res; 178511b41d2SMark Murray 179511b41d2SMark Murray /* 180511b41d2SMark Murray * If we are running as root and want to connect to a privileged 181511b41d2SMark Murray * port, bind our own socket to a privileged port. 182511b41d2SMark Murray */ 183511b41d2SMark Murray if (privileged) { 184511b41d2SMark Murray int p = IPPORT_RESERVED - 1; 18580628bacSDag-Erling Smørgrav PRIV_START; 186cf2b5f3bSDag-Erling Smørgrav sock = rresvport_af(&p, ai->ai_family); 18780628bacSDag-Erling Smørgrav PRIV_END; 188511b41d2SMark Murray if (sock < 0) 189cf2b5f3bSDag-Erling Smørgrav error("rresvport: af=%d %.100s", ai->ai_family, 190cf2b5f3bSDag-Erling Smørgrav strerror(errno)); 191511b41d2SMark Murray else 192511b41d2SMark Murray debug("Allocated local port %d.", p); 193af12a3e7SDag-Erling Smørgrav return sock; 194af12a3e7SDag-Erling Smørgrav } 195cf2b5f3bSDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 196511b41d2SMark Murray if (sock < 0) 197511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 198af12a3e7SDag-Erling Smørgrav 199af12a3e7SDag-Erling Smørgrav /* Bind the socket to an alternative local IP address */ 200af12a3e7SDag-Erling Smørgrav if (options.bind_address == NULL) 201af12a3e7SDag-Erling Smørgrav return sock; 202af12a3e7SDag-Erling Smørgrav 203af12a3e7SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 204cf2b5f3bSDag-Erling Smørgrav hints.ai_family = ai->ai_family; 205cf2b5f3bSDag-Erling Smørgrav hints.ai_socktype = ai->ai_socktype; 206cf2b5f3bSDag-Erling Smørgrav hints.ai_protocol = ai->ai_protocol; 207af12a3e7SDag-Erling Smørgrav hints.ai_flags = AI_PASSIVE; 208af12a3e7SDag-Erling Smørgrav gaierr = getaddrinfo(options.bind_address, "0", &hints, &res); 209af12a3e7SDag-Erling Smørgrav if (gaierr) { 210af12a3e7SDag-Erling Smørgrav error("getaddrinfo: %s: %s", options.bind_address, 211af12a3e7SDag-Erling Smørgrav gai_strerror(gaierr)); 212af12a3e7SDag-Erling Smørgrav close(sock); 213af12a3e7SDag-Erling Smørgrav return -1; 214511b41d2SMark Murray } 215af12a3e7SDag-Erling Smørgrav if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 216af12a3e7SDag-Erling Smørgrav error("bind: %s: %s", options.bind_address, strerror(errno)); 217af12a3e7SDag-Erling Smørgrav close(sock); 218af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 219af12a3e7SDag-Erling Smørgrav return -1; 220af12a3e7SDag-Erling Smørgrav } 221af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 222511b41d2SMark Murray return sock; 223511b41d2SMark Murray } 224511b41d2SMark Murray 225cf2b5f3bSDag-Erling Smørgrav static int 226cf2b5f3bSDag-Erling Smørgrav timeout_connect(int sockfd, const struct sockaddr *serv_addr, 227cf2b5f3bSDag-Erling Smørgrav socklen_t addrlen, int timeout) 228cf2b5f3bSDag-Erling Smørgrav { 229cf2b5f3bSDag-Erling Smørgrav fd_set *fdset; 230cf2b5f3bSDag-Erling Smørgrav struct timeval tv; 231cf2b5f3bSDag-Erling Smørgrav socklen_t optlen; 232cf2b5f3bSDag-Erling Smørgrav int fdsetsz, optval, rc, result = -1; 233cf2b5f3bSDag-Erling Smørgrav 234cf2b5f3bSDag-Erling Smørgrav if (timeout <= 0) 235cf2b5f3bSDag-Erling Smørgrav return (connect(sockfd, serv_addr, addrlen)); 236cf2b5f3bSDag-Erling Smørgrav 237cf2b5f3bSDag-Erling Smørgrav if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) 238cf2b5f3bSDag-Erling Smørgrav return (-1); 239cf2b5f3bSDag-Erling Smørgrav 240cf2b5f3bSDag-Erling Smørgrav rc = connect(sockfd, serv_addr, addrlen); 241cf2b5f3bSDag-Erling Smørgrav if (rc == 0) 242cf2b5f3bSDag-Erling Smørgrav return (0); 243cf2b5f3bSDag-Erling Smørgrav if (errno != EINPROGRESS) 244cf2b5f3bSDag-Erling Smørgrav return (-1); 245cf2b5f3bSDag-Erling Smørgrav 246cf2b5f3bSDag-Erling Smørgrav fdsetsz = howmany(sockfd + 1, NFDBITS) * sizeof(fd_mask); 247cf2b5f3bSDag-Erling Smørgrav fdset = (fd_set *)xmalloc(fdsetsz); 248cf2b5f3bSDag-Erling Smørgrav 249cf2b5f3bSDag-Erling Smørgrav memset(fdset, 0, fdsetsz); 250cf2b5f3bSDag-Erling Smørgrav FD_SET(sockfd, fdset); 251cf2b5f3bSDag-Erling Smørgrav tv.tv_sec = timeout; 252cf2b5f3bSDag-Erling Smørgrav tv.tv_usec = 0; 253cf2b5f3bSDag-Erling Smørgrav 254cf2b5f3bSDag-Erling Smørgrav for(;;) { 255cf2b5f3bSDag-Erling Smørgrav rc = select(sockfd + 1, NULL, fdset, NULL, &tv); 256cf2b5f3bSDag-Erling Smørgrav if (rc != -1 || errno != EINTR) 257cf2b5f3bSDag-Erling Smørgrav break; 258cf2b5f3bSDag-Erling Smørgrav } 259cf2b5f3bSDag-Erling Smørgrav 260cf2b5f3bSDag-Erling Smørgrav switch(rc) { 261cf2b5f3bSDag-Erling Smørgrav case 0: 262cf2b5f3bSDag-Erling Smørgrav /* Timed out */ 263cf2b5f3bSDag-Erling Smørgrav errno = ETIMEDOUT; 264cf2b5f3bSDag-Erling Smørgrav break; 265cf2b5f3bSDag-Erling Smørgrav case -1: 266cf2b5f3bSDag-Erling Smørgrav /* Select error */ 267cf2b5f3bSDag-Erling Smørgrav debug("select: %s", strerror(errno)); 268cf2b5f3bSDag-Erling Smørgrav break; 269cf2b5f3bSDag-Erling Smørgrav case 1: 270cf2b5f3bSDag-Erling Smørgrav /* Completed or failed */ 271cf2b5f3bSDag-Erling Smørgrav optval = 0; 272cf2b5f3bSDag-Erling Smørgrav optlen = sizeof(optval); 273cf2b5f3bSDag-Erling Smørgrav if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, 274cf2b5f3bSDag-Erling Smørgrav &optlen) == -1) { 275cf2b5f3bSDag-Erling Smørgrav debug("getsockopt: %s", strerror(errno)); 276cf2b5f3bSDag-Erling Smørgrav break; 277cf2b5f3bSDag-Erling Smørgrav } 278cf2b5f3bSDag-Erling Smørgrav if (optval != 0) { 279cf2b5f3bSDag-Erling Smørgrav errno = optval; 280cf2b5f3bSDag-Erling Smørgrav break; 281cf2b5f3bSDag-Erling Smørgrav } 282cf2b5f3bSDag-Erling Smørgrav result = 0; 283cf2b5f3bSDag-Erling Smørgrav break; 284cf2b5f3bSDag-Erling Smørgrav default: 285cf2b5f3bSDag-Erling Smørgrav /* Should not occur */ 286cf2b5f3bSDag-Erling Smørgrav fatal("Bogus return (%d) from select()", rc); 287cf2b5f3bSDag-Erling Smørgrav } 288cf2b5f3bSDag-Erling Smørgrav 289cf2b5f3bSDag-Erling Smørgrav xfree(fdset); 290cf2b5f3bSDag-Erling Smørgrav return (result); 291cf2b5f3bSDag-Erling Smørgrav } 292cf2b5f3bSDag-Erling Smørgrav 293511b41d2SMark Murray /* 294511b41d2SMark Murray * Opens a TCP/IP connection to the remote server on the given host. 295511b41d2SMark Murray * The address of the remote host will be returned in hostaddr. 29680628bacSDag-Erling Smørgrav * If port is 0, the default port will be used. If needpriv is true, 297511b41d2SMark Murray * a privileged port will be allocated to make the connection. 29880628bacSDag-Erling Smørgrav * This requires super-user privileges if needpriv is true. 299511b41d2SMark Murray * Connection_attempts specifies the maximum number of tries (one per 300511b41d2SMark Murray * second). If proxy_command is non-NULL, it specifies the command (with %h 301511b41d2SMark Murray * and %p substituted for host and port, respectively) to use to contact 302511b41d2SMark Murray * the daemon. 303af12a3e7SDag-Erling Smørgrav * Return values: 304af12a3e7SDag-Erling Smørgrav * 0 for OK 305af12a3e7SDag-Erling Smørgrav * ECONNREFUSED if we got a "Connection Refused" by the peer on any address 306af12a3e7SDag-Erling Smørgrav * ECONNABORTED if we failed without a "Connection refused" 307af12a3e7SDag-Erling Smørgrav * Suitable error messages for the connection failure will already have been 308af12a3e7SDag-Erling Smørgrav * printed. 309511b41d2SMark Murray */ 310511b41d2SMark Murray int 3111f5ce8f4SBrian Feldman ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 312af12a3e7SDag-Erling Smørgrav u_short port, int family, int connection_attempts, 31380628bacSDag-Erling Smørgrav int needpriv, const char *proxy_command) 314511b41d2SMark Murray { 315511b41d2SMark Murray int gaierr; 316ca3176e7SBrian Feldman int on = 1; 317ca3176e7SBrian Feldman int sock = -1, attempt; 318ca3176e7SBrian Feldman char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 319ca3176e7SBrian Feldman struct addrinfo hints, *ai, *aitop; 320ca3176e7SBrian Feldman struct servent *sp; 321af12a3e7SDag-Erling Smørgrav /* 322af12a3e7SDag-Erling Smørgrav * Did we get only other errors than "Connection refused" (which 323af12a3e7SDag-Erling Smørgrav * should block fallback to rsh and similar), or did we get at least 324af12a3e7SDag-Erling Smørgrav * one "Connection refused"? 325af12a3e7SDag-Erling Smørgrav */ 326af12a3e7SDag-Erling Smørgrav int full_failure = 1; 327511b41d2SMark Murray 328e73e9afaSDag-Erling Smørgrav debug2("ssh_connect: needpriv %d", needpriv); 329511b41d2SMark Murray 330511b41d2SMark Murray /* Get default port if port has not been set. */ 331511b41d2SMark Murray if (port == 0) { 332511b41d2SMark Murray sp = getservbyname(SSH_SERVICE_NAME, "tcp"); 333511b41d2SMark Murray if (sp) 334511b41d2SMark Murray port = ntohs(sp->s_port); 335511b41d2SMark Murray else 336511b41d2SMark Murray port = SSH_DEFAULT_PORT; 337511b41d2SMark Murray } 338511b41d2SMark Murray /* If a proxy command is given, connect using it. */ 339511b41d2SMark Murray if (proxy_command != NULL) 34080628bacSDag-Erling Smørgrav return ssh_proxy_connect(host, port, proxy_command); 341511b41d2SMark Murray 342511b41d2SMark Murray /* No proxy command. */ 343511b41d2SMark Murray 344511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 345af12a3e7SDag-Erling Smørgrav hints.ai_family = family; 346511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 347a82e551fSDag-Erling Smørgrav snprintf(strport, sizeof strport, "%u", port); 3481f5ce8f4SBrian Feldman if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 3491f5ce8f4SBrian Feldman fatal("%s: %.100s: %s", __progname, host, 350511b41d2SMark Murray gai_strerror(gaierr)); 351511b41d2SMark Murray 352511b41d2SMark Murray /* 353511b41d2SMark Murray * Try to connect several times. On some machines, the first time 354511b41d2SMark Murray * will sometimes fail. In general socket code appears to behave 355511b41d2SMark Murray * quite magically on many machines. 356511b41d2SMark Murray */ 357af12a3e7SDag-Erling Smørgrav for (attempt = 0; ;) { 358511b41d2SMark Murray if (attempt > 0) 359511b41d2SMark Murray debug("Trying again..."); 360511b41d2SMark Murray 361511b41d2SMark Murray /* Loop through addresses for this host, and try each one in 362511b41d2SMark Murray sequence until the connection succeeds. */ 363511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 364511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 365511b41d2SMark Murray continue; 366511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 367511b41d2SMark Murray ntop, sizeof(ntop), strport, sizeof(strport), 368511b41d2SMark Murray NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 369511b41d2SMark Murray error("ssh_connect: getnameinfo failed"); 370511b41d2SMark Murray continue; 371511b41d2SMark Murray } 372511b41d2SMark Murray debug("Connecting to %.200s [%.100s] port %s.", 3731f5ce8f4SBrian Feldman host, ntop, strport); 374511b41d2SMark Murray 375511b41d2SMark Murray /* Create a socket for connecting. */ 376cf2b5f3bSDag-Erling Smørgrav sock = ssh_create_socket(needpriv, ai); 377511b41d2SMark Murray if (sock < 0) 378af12a3e7SDag-Erling Smørgrav /* Any error is already output */ 379511b41d2SMark Murray continue; 380511b41d2SMark Murray 381cf2b5f3bSDag-Erling Smørgrav if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 382cf2b5f3bSDag-Erling Smørgrav options.connection_timeout) >= 0) { 383511b41d2SMark Murray /* Successful connection. */ 384c322fe35SKris Kennaway memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 385511b41d2SMark Murray break; 386511b41d2SMark Murray } else { 387af12a3e7SDag-Erling Smørgrav if (errno == ECONNREFUSED) 388af12a3e7SDag-Erling Smørgrav full_failure = 0; 389f388f5efSDag-Erling Smørgrav debug("connect to address %s port %s: %s", 390f388f5efSDag-Erling Smørgrav ntop, strport, strerror(errno)); 391511b41d2SMark Murray /* 392511b41d2SMark Murray * Close the failed socket; there appear to 393511b41d2SMark Murray * be some problems when reusing a socket for 394511b41d2SMark Murray * which connect() has already returned an 395511b41d2SMark Murray * error. 396511b41d2SMark Murray */ 397511b41d2SMark Murray close(sock); 398511b41d2SMark Murray } 399511b41d2SMark Murray } 4001f5ce8f4SBrian Feldman if (ai) 401511b41d2SMark Murray break; /* Successful connection. */ 402511b41d2SMark Murray 403af12a3e7SDag-Erling Smørgrav attempt++; 404af12a3e7SDag-Erling Smørgrav if (attempt >= connection_attempts) 405af12a3e7SDag-Erling Smørgrav break; 406511b41d2SMark Murray /* Sleep a moment before retrying. */ 407511b41d2SMark Murray sleep(1); 408511b41d2SMark Murray } 409511b41d2SMark Murray 410511b41d2SMark Murray freeaddrinfo(aitop); 411511b41d2SMark Murray 412511b41d2SMark Murray /* Return failure if we didn't get a successful connection. */ 413f388f5efSDag-Erling Smørgrav if (attempt >= connection_attempts) { 414cf2b5f3bSDag-Erling Smørgrav logit("ssh: connect to host %s port %s: %s", 415f388f5efSDag-Erling Smørgrav host, strport, strerror(errno)); 416af12a3e7SDag-Erling Smørgrav return full_failure ? ECONNABORTED : ECONNREFUSED; 417f388f5efSDag-Erling Smørgrav } 418511b41d2SMark Murray 419511b41d2SMark Murray debug("Connection established."); 420511b41d2SMark Murray 421ca3176e7SBrian Feldman /* Set keepalives if requested. */ 422ca3176e7SBrian Feldman if (options.keepalives && 423ca3176e7SBrian Feldman setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 424ca3176e7SBrian Feldman sizeof(on)) < 0) 425ca3176e7SBrian Feldman error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 426ca3176e7SBrian Feldman 427511b41d2SMark Murray /* Set the connection. */ 428511b41d2SMark Murray packet_set_connection(sock, sock); 429511b41d2SMark Murray 430af12a3e7SDag-Erling Smørgrav return 0; 431511b41d2SMark Murray } 432511b41d2SMark Murray 433511b41d2SMark Murray /* 434e8aafc91SKris Kennaway * Waits for the server identification string, and sends our own 435e8aafc91SKris Kennaway * identification string. 436511b41d2SMark Murray */ 437af12a3e7SDag-Erling Smørgrav static void 438ca3176e7SBrian Feldman ssh_exchange_identification(void) 439511b41d2SMark Murray { 440e8aafc91SKris Kennaway char buf[256], remote_version[256]; /* must be same size! */ 441e8aafc91SKris Kennaway int remote_major, remote_minor, i, mismatch; 442e8aafc91SKris Kennaway int connection_in = packet_get_connection_in(); 443e8aafc91SKris Kennaway int connection_out = packet_get_connection_out(); 444ca3176e7SBrian Feldman int minor1 = PROTOCOL_MINOR_1; 445511b41d2SMark Murray 446e8aafc91SKris Kennaway /* Read other side\'s version identification. */ 447c2d3a559SKris Kennaway for (;;) { 448e8aafc91SKris Kennaway for (i = 0; i < sizeof(buf) - 1; i++) { 449c2d3a559SKris Kennaway int len = atomicio(read, connection_in, &buf[i], 1); 450e8aafc91SKris Kennaway if (len < 0) 451e8aafc91SKris Kennaway fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); 452e8aafc91SKris Kennaway if (len != 1) 453e8aafc91SKris Kennaway fatal("ssh_exchange_identification: Connection closed by remote host"); 454e8aafc91SKris Kennaway if (buf[i] == '\r') { 455e8aafc91SKris Kennaway buf[i] = '\n'; 456e8aafc91SKris Kennaway buf[i + 1] = 0; 457e8aafc91SKris Kennaway continue; /**XXX wait for \n */ 458511b41d2SMark Murray } 459e8aafc91SKris Kennaway if (buf[i] == '\n') { 460e8aafc91SKris Kennaway buf[i + 1] = 0; 461511b41d2SMark Murray break; 462e8aafc91SKris Kennaway } 463e8aafc91SKris Kennaway } 464e8aafc91SKris Kennaway buf[sizeof(buf) - 1] = 0; 465c2d3a559SKris Kennaway if (strncmp(buf, "SSH-", 4) == 0) 466c2d3a559SKris Kennaway break; 467c2d3a559SKris Kennaway debug("ssh_exchange_identification: %s", buf); 468c2d3a559SKris Kennaway } 469e8aafc91SKris Kennaway server_version_string = xstrdup(buf); 470511b41d2SMark Murray 471511b41d2SMark Murray /* 472e8aafc91SKris Kennaway * Check that the versions match. In future this might accept 473e8aafc91SKris Kennaway * several versions and set appropriate flags to handle them. 474511b41d2SMark Murray */ 475e8aafc91SKris Kennaway if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 476e8aafc91SKris Kennaway &remote_major, &remote_minor, remote_version) != 3) 477e8aafc91SKris Kennaway fatal("Bad remote protocol version identification: '%.100s'", buf); 478e8aafc91SKris Kennaway debug("Remote protocol version %d.%d, remote software version %.100s", 479e8aafc91SKris Kennaway remote_major, remote_minor, remote_version); 480511b41d2SMark Murray 481e8aafc91SKris Kennaway compat_datafellows(remote_version); 482e8aafc91SKris Kennaway mismatch = 0; 483e8aafc91SKris Kennaway 484e8aafc91SKris Kennaway switch (remote_major) { 485e8aafc91SKris Kennaway case 1: 486e8aafc91SKris Kennaway if (remote_minor == 99 && 487e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) && 488e8aafc91SKris Kennaway !(options.protocol & SSH_PROTO_1_PREFERRED)) { 489e8aafc91SKris Kennaway enable_compat20(); 490511b41d2SMark Murray break; 491e8aafc91SKris Kennaway } 492e8aafc91SKris Kennaway if (!(options.protocol & SSH_PROTO_1)) { 493e8aafc91SKris Kennaway mismatch = 1; 494e8aafc91SKris Kennaway break; 495e8aafc91SKris Kennaway } 496e8aafc91SKris Kennaway if (remote_minor < 3) { 497e8aafc91SKris Kennaway fatal("Remote machine has too old SSH software version."); 498ca3176e7SBrian Feldman } else if (remote_minor == 3 || remote_minor == 4) { 499e8aafc91SKris Kennaway /* We speak 1.3, too. */ 500e8aafc91SKris Kennaway enable_compat13(); 501ca3176e7SBrian Feldman minor1 = 3; 502e8aafc91SKris Kennaway if (options.forward_agent) { 503cf2b5f3bSDag-Erling Smørgrav logit("Agent forwarding disabled for protocol 1.3"); 504e8aafc91SKris Kennaway options.forward_agent = 0; 505e8aafc91SKris Kennaway } 506e8aafc91SKris Kennaway } 507e8aafc91SKris Kennaway break; 508e8aafc91SKris Kennaway case 2: 509e8aafc91SKris Kennaway if (options.protocol & SSH_PROTO_2) { 510e8aafc91SKris Kennaway enable_compat20(); 511e8aafc91SKris Kennaway break; 512e8aafc91SKris Kennaway } 513e8aafc91SKris Kennaway /* FALLTHROUGH */ 514511b41d2SMark Murray default: 515e8aafc91SKris Kennaway mismatch = 1; 516e8aafc91SKris Kennaway break; 517511b41d2SMark Murray } 518e8aafc91SKris Kennaway if (mismatch) 519e8aafc91SKris Kennaway fatal("Protocol major versions differ: %d vs. %d", 520e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 521e8aafc91SKris Kennaway remote_major); 522e8aafc91SKris Kennaway /* Send our own protocol version identification. */ 523e8aafc91SKris Kennaway snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 524e8aafc91SKris Kennaway compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 525ca3176e7SBrian Feldman compat20 ? PROTOCOL_MINOR_2 : minor1, 526e8aafc91SKris Kennaway SSH_VERSION); 527cf2b5f3bSDag-Erling Smørgrav if (atomicio(vwrite, connection_out, buf, strlen(buf)) != strlen(buf)) 528e8aafc91SKris Kennaway fatal("write: %.100s", strerror(errno)); 529e8aafc91SKris Kennaway client_version_string = xstrdup(buf); 530e8aafc91SKris Kennaway chop(client_version_string); 531e8aafc91SKris Kennaway chop(server_version_string); 532e8aafc91SKris Kennaway debug("Local version string %.100s", client_version_string); 533511b41d2SMark Murray } 534511b41d2SMark Murray 535ca3176e7SBrian Feldman /* defaults to 'no' */ 536af12a3e7SDag-Erling Smørgrav static int 537af12a3e7SDag-Erling Smørgrav confirm(const char *prompt) 538511b41d2SMark Murray { 539af12a3e7SDag-Erling Smørgrav const char *msg, *again = "Please type 'yes' or 'no': "; 540af12a3e7SDag-Erling Smørgrav char *p; 541af12a3e7SDag-Erling Smørgrav int ret = -1; 542511b41d2SMark Murray 543ca3176e7SBrian Feldman if (options.batch_mode) 544ca3176e7SBrian Feldman return 0; 545af12a3e7SDag-Erling Smørgrav for (msg = prompt;;msg = again) { 546af12a3e7SDag-Erling Smørgrav p = read_passphrase(msg, RP_ECHO); 547af12a3e7SDag-Erling Smørgrav if (p == NULL || 548af12a3e7SDag-Erling Smørgrav (p[0] == '\0') || (p[0] == '\n') || 549af12a3e7SDag-Erling Smørgrav strncasecmp(p, "no", 2) == 0) 550af12a3e7SDag-Erling Smørgrav ret = 0; 551f388f5efSDag-Erling Smørgrav if (p && strncasecmp(p, "yes", 3) == 0) 552af12a3e7SDag-Erling Smørgrav ret = 1; 553af12a3e7SDag-Erling Smørgrav if (p) 554af12a3e7SDag-Erling Smørgrav xfree(p); 555af12a3e7SDag-Erling Smørgrav if (ret != -1) 556af12a3e7SDag-Erling Smørgrav return ret; 557511b41d2SMark Murray } 558511b41d2SMark Murray } 559511b41d2SMark Murray 560e8aafc91SKris Kennaway /* 561af12a3e7SDag-Erling Smørgrav * check whether the supplied host key is valid, return -1 if the key 562af12a3e7SDag-Erling Smørgrav * is not valid. the user_hostfile will not be updated if 'readonly' is true. 563e8aafc91SKris Kennaway */ 564af12a3e7SDag-Erling Smørgrav static int 565e8aafc91SKris Kennaway check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, 566af12a3e7SDag-Erling Smørgrav int readonly, const char *user_hostfile, const char *system_hostfile) 567511b41d2SMark Murray { 568e8aafc91SKris Kennaway Key *file_key; 569e8aafc91SKris Kennaway char *type = key_type(host_key); 570e8aafc91SKris Kennaway char *ip = NULL; 571ca3176e7SBrian Feldman char hostline[1000], *hostp, *fp; 572e8aafc91SKris Kennaway HostStatus host_status; 573e8aafc91SKris Kennaway HostStatus ip_status; 574e8aafc91SKris Kennaway int local = 0, host_ip_differ = 0; 575989dd127SDag-Erling Smørgrav int salen; 576e8aafc91SKris Kennaway char ntop[NI_MAXHOST]; 577af12a3e7SDag-Erling Smørgrav char msg[1024]; 578cf2b5f3bSDag-Erling Smørgrav int len, host_line, ip_line; 579ca3176e7SBrian Feldman const char *host_file = NULL, *ip_file = NULL; 580511b41d2SMark Murray 581e8aafc91SKris Kennaway /* 582e8aafc91SKris Kennaway * Force accepting of the host key for loopback/localhost. The 583e8aafc91SKris Kennaway * problem is that if the home directory is NFS-mounted to multiple 584e8aafc91SKris Kennaway * machines, localhost will refer to a different machine in each of 585e8aafc91SKris Kennaway * them, and the user will get bogus HOST_CHANGED warnings. This 586e8aafc91SKris Kennaway * essentially disables host authentication for localhost; however, 587e8aafc91SKris Kennaway * this is probably not a real problem. 588e8aafc91SKris Kennaway */ 589e8aafc91SKris Kennaway /** hostaddr == 0! */ 590e8aafc91SKris Kennaway switch (hostaddr->sa_family) { 591e8aafc91SKris Kennaway case AF_INET: 592af12a3e7SDag-Erling Smørgrav local = (ntohl(((struct sockaddr_in *)hostaddr)-> 593af12a3e7SDag-Erling Smørgrav sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 594989dd127SDag-Erling Smørgrav salen = sizeof(struct sockaddr_in); 595511b41d2SMark Murray break; 596e8aafc91SKris Kennaway case AF_INET6: 597af12a3e7SDag-Erling Smørgrav local = IN6_IS_ADDR_LOOPBACK( 598af12a3e7SDag-Erling Smørgrav &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 599989dd127SDag-Erling Smørgrav salen = sizeof(struct sockaddr_in6); 600511b41d2SMark Murray break; 601e8aafc91SKris Kennaway default: 602e8aafc91SKris Kennaway local = 0; 603989dd127SDag-Erling Smørgrav salen = sizeof(struct sockaddr_storage); 604511b41d2SMark Murray break; 605511b41d2SMark Murray } 606af12a3e7SDag-Erling Smørgrav if (options.no_host_authentication_for_localhost == 1 && local && 607af12a3e7SDag-Erling Smørgrav options.host_key_alias == NULL) { 608ca3176e7SBrian Feldman debug("Forcing accepting of host key for " 609ca3176e7SBrian Feldman "loopback/localhost."); 610af12a3e7SDag-Erling Smørgrav return 0; 611511b41d2SMark Murray } 612511b41d2SMark Murray 613e8aafc91SKris Kennaway /* 614ca3176e7SBrian Feldman * We don't have the remote ip-address for connections 615ca3176e7SBrian Feldman * using a proxy command 616e8aafc91SKris Kennaway */ 617ca3176e7SBrian Feldman if (options.proxy_command == NULL) { 618989dd127SDag-Erling Smørgrav if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop), 619e8aafc91SKris Kennaway NULL, 0, NI_NUMERICHOST) != 0) 620e8aafc91SKris Kennaway fatal("check_host_key: getnameinfo failed"); 621e8aafc91SKris Kennaway ip = xstrdup(ntop); 622ca3176e7SBrian Feldman } else { 623ca3176e7SBrian Feldman ip = xstrdup("<no hostip for proxy command>"); 624ca3176e7SBrian Feldman } 625ca3176e7SBrian Feldman /* 626ca3176e7SBrian Feldman * Turn off check_host_ip if the connection is to localhost, via proxy 627ca3176e7SBrian Feldman * command or if we don't have a hostname to compare with 628ca3176e7SBrian Feldman */ 629ca3176e7SBrian Feldman if (options.check_host_ip && 630ca3176e7SBrian Feldman (local || strcmp(host, ip) == 0 || options.proxy_command != NULL)) 631ca3176e7SBrian Feldman options.check_host_ip = 0; 632ca3176e7SBrian Feldman 633ca3176e7SBrian Feldman /* 634ca3176e7SBrian Feldman * Allow the user to record the key under a different name. This is 635ca3176e7SBrian Feldman * useful for ssh tunneling over forwarded connections or if you run 636ca3176e7SBrian Feldman * multiple sshd's on different ports on the same machine. 637ca3176e7SBrian Feldman */ 638ca3176e7SBrian Feldman if (options.host_key_alias != NULL) { 639ca3176e7SBrian Feldman host = options.host_key_alias; 640ca3176e7SBrian Feldman debug("using hostkeyalias: %s", host); 641e8aafc91SKris Kennaway } 642e8aafc91SKris Kennaway 643e8aafc91SKris Kennaway /* 644e8aafc91SKris Kennaway * Store the host key from the known host file in here so that we can 645e8aafc91SKris Kennaway * compare it with the key for the IP address. 646e8aafc91SKris Kennaway */ 647e8aafc91SKris Kennaway file_key = key_new(host_key->type); 648e8aafc91SKris Kennaway 649e8aafc91SKris Kennaway /* 650e8aafc91SKris Kennaway * Check if the host key is present in the user\'s list of known 651e8aafc91SKris Kennaway * hosts or in the systemwide list. 652e8aafc91SKris Kennaway */ 653ca3176e7SBrian Feldman host_file = user_hostfile; 654af12a3e7SDag-Erling Smørgrav host_status = check_host_in_hostfile(host_file, host, host_key, 655af12a3e7SDag-Erling Smørgrav file_key, &host_line); 656ca3176e7SBrian Feldman if (host_status == HOST_NEW) { 657ca3176e7SBrian Feldman host_file = system_hostfile; 658af12a3e7SDag-Erling Smørgrav host_status = check_host_in_hostfile(host_file, host, host_key, 659af12a3e7SDag-Erling Smørgrav file_key, &host_line); 660ca3176e7SBrian Feldman } 661e8aafc91SKris Kennaway /* 662e8aafc91SKris Kennaway * Also perform check for the ip address, skip the check if we are 663e8aafc91SKris Kennaway * localhost or the hostname was an ip address to begin with 664e8aafc91SKris Kennaway */ 665ca3176e7SBrian Feldman if (options.check_host_ip) { 666e8aafc91SKris Kennaway Key *ip_key = key_new(host_key->type); 667e8aafc91SKris Kennaway 668ca3176e7SBrian Feldman ip_file = user_hostfile; 669af12a3e7SDag-Erling Smørgrav ip_status = check_host_in_hostfile(ip_file, ip, host_key, 670af12a3e7SDag-Erling Smørgrav ip_key, &ip_line); 671ca3176e7SBrian Feldman if (ip_status == HOST_NEW) { 672ca3176e7SBrian Feldman ip_file = system_hostfile; 673af12a3e7SDag-Erling Smørgrav ip_status = check_host_in_hostfile(ip_file, ip, 674af12a3e7SDag-Erling Smørgrav host_key, ip_key, &ip_line); 675ca3176e7SBrian Feldman } 676e8aafc91SKris Kennaway if (host_status == HOST_CHANGED && 677e8aafc91SKris Kennaway (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) 678e8aafc91SKris Kennaway host_ip_differ = 1; 679e8aafc91SKris Kennaway 680e8aafc91SKris Kennaway key_free(ip_key); 681e8aafc91SKris Kennaway } else 682e8aafc91SKris Kennaway ip_status = host_status; 683e8aafc91SKris Kennaway 684e8aafc91SKris Kennaway key_free(file_key); 685e8aafc91SKris Kennaway 686e8aafc91SKris Kennaway switch (host_status) { 687e8aafc91SKris Kennaway case HOST_OK: 688e8aafc91SKris Kennaway /* The host is known and the key matches. */ 689e8aafc91SKris Kennaway debug("Host '%.200s' is known and matches the %s host key.", 690e8aafc91SKris Kennaway host, type); 691ca3176e7SBrian Feldman debug("Found key in %s:%d", host_file, host_line); 692ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 693af12a3e7SDag-Erling Smørgrav if (readonly) 694cf2b5f3bSDag-Erling Smørgrav logit("%s host key for IP address " 695af12a3e7SDag-Erling Smørgrav "'%.128s' not in list of known hosts.", 696e8aafc91SKris Kennaway type, ip); 697af12a3e7SDag-Erling Smørgrav else if (!add_host_to_hostfile(user_hostfile, ip, 698af12a3e7SDag-Erling Smørgrav host_key)) 699cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the %s host key for IP " 700af12a3e7SDag-Erling Smørgrav "address '%.128s' to the list of known " 701af12a3e7SDag-Erling Smørgrav "hosts (%.30s).", type, ip, user_hostfile); 702af12a3e7SDag-Erling Smørgrav else 703cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added the %s host " 704af12a3e7SDag-Erling Smørgrav "key for IP address '%.128s' to the list " 705af12a3e7SDag-Erling Smørgrav "of known hosts.", type, ip); 706e8aafc91SKris Kennaway } 707e8aafc91SKris Kennaway break; 708e8aafc91SKris Kennaway case HOST_NEW: 709af12a3e7SDag-Erling Smørgrav if (readonly) 710af12a3e7SDag-Erling Smørgrav goto fail; 711e8aafc91SKris Kennaway /* The host is new. */ 712e8aafc91SKris Kennaway if (options.strict_host_key_checking == 1) { 713af12a3e7SDag-Erling Smørgrav /* 714af12a3e7SDag-Erling Smørgrav * User has requested strict host key checking. We 715af12a3e7SDag-Erling Smørgrav * will not add the host key automatically. The only 716af12a3e7SDag-Erling Smørgrav * alternative left is to abort. 717af12a3e7SDag-Erling Smørgrav */ 718af12a3e7SDag-Erling Smørgrav error("No %s host key is known for %.200s and you " 719af12a3e7SDag-Erling Smørgrav "have requested strict checking.", type, host); 720af12a3e7SDag-Erling Smørgrav goto fail; 721e8aafc91SKris Kennaway } else if (options.strict_host_key_checking == 2) { 722cf2b5f3bSDag-Erling Smørgrav char msg1[1024], msg2[1024]; 723cf2b5f3bSDag-Erling Smørgrav 724cf2b5f3bSDag-Erling Smørgrav if (show_other_keys(host, host_key)) 725cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), 726cf2b5f3bSDag-Erling Smørgrav "\nbut keys of different type are already" 727cf2b5f3bSDag-Erling Smørgrav " known for this host."); 728cf2b5f3bSDag-Erling Smørgrav else 729cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), "."); 730e8aafc91SKris Kennaway /* The default */ 731ca3176e7SBrian Feldman fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 732cf2b5f3bSDag-Erling Smørgrav msg2[0] = '\0'; 733cf2b5f3bSDag-Erling Smørgrav #ifdef DNS 734cf2b5f3bSDag-Erling Smørgrav if (options.verify_host_key_dns) { 735cf2b5f3bSDag-Erling Smørgrav if (verified_host_key_dns) 736cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 737cf2b5f3bSDag-Erling Smørgrav "Matching host key fingerprint" 738cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 739cf2b5f3bSDag-Erling Smørgrav else 740cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 741cf2b5f3bSDag-Erling Smørgrav "No matching host key fingerprint" 742cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 743cf2b5f3bSDag-Erling Smørgrav } 744cf2b5f3bSDag-Erling Smørgrav #endif 745af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 746af12a3e7SDag-Erling Smørgrav "The authenticity of host '%.200s (%s)' can't be " 747f388f5efSDag-Erling Smørgrav "established%s\n" 748cf2b5f3bSDag-Erling Smørgrav "%s key fingerprint is %s.\n%s" 749af12a3e7SDag-Erling Smørgrav "Are you sure you want to continue connecting " 750f388f5efSDag-Erling Smørgrav "(yes/no)? ", 751cf2b5f3bSDag-Erling Smørgrav host, ip, msg1, type, fp, msg2); 752ca3176e7SBrian Feldman xfree(fp); 753af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 754af12a3e7SDag-Erling Smørgrav goto fail; 755e8aafc91SKris Kennaway } 756ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 757e8aafc91SKris Kennaway snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 758e8aafc91SKris Kennaway hostp = hostline; 759e8aafc91SKris Kennaway } else 760e8aafc91SKris Kennaway hostp = host; 761e8aafc91SKris Kennaway 762af12a3e7SDag-Erling Smørgrav /* 763af12a3e7SDag-Erling Smørgrav * If not in strict mode, add the key automatically to the 764af12a3e7SDag-Erling Smørgrav * local known_hosts file. 765af12a3e7SDag-Erling Smørgrav */ 766e8aafc91SKris Kennaway if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) 767cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the host to the list of known " 768af12a3e7SDag-Erling Smørgrav "hosts (%.500s).", user_hostfile); 769e8aafc91SKris Kennaway else 770cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added '%.200s' (%s) to the " 771af12a3e7SDag-Erling Smørgrav "list of known hosts.", hostp, type); 772e8aafc91SKris Kennaway break; 773e8aafc91SKris Kennaway case HOST_CHANGED: 774e8aafc91SKris Kennaway if (options.check_host_ip && host_ip_differ) { 775e8aafc91SKris Kennaway char *msg; 776e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 777e8aafc91SKris Kennaway msg = "is unknown"; 778e8aafc91SKris Kennaway else if (ip_status == HOST_OK) 779e8aafc91SKris Kennaway msg = "is unchanged"; 780e8aafc91SKris Kennaway else 781e8aafc91SKris Kennaway msg = "has a different value"; 782e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 783e8aafc91SKris Kennaway error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 784e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 785e8aafc91SKris Kennaway error("The %s host key for %s has changed,", type, host); 786e8aafc91SKris Kennaway error("and the key for the according IP address %s", ip); 787e8aafc91SKris Kennaway error("%s. This could either mean that", msg); 788e8aafc91SKris Kennaway error("DNS SPOOFING is happening or the IP address for the host"); 789ca3176e7SBrian Feldman error("and its host key have changed at the same time."); 790ca3176e7SBrian Feldman if (ip_status != HOST_NEW) 791ca3176e7SBrian Feldman error("Offending key for IP in %s:%d", ip_file, ip_line); 792e8aafc91SKris Kennaway } 793e8aafc91SKris Kennaway /* The host key has changed. */ 794ca3176e7SBrian Feldman fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 795e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 796e8aafc91SKris Kennaway error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 797e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 798e8aafc91SKris Kennaway error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 799e8aafc91SKris Kennaway error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 800e8aafc91SKris Kennaway error("It is also possible that the %s host key has just been changed.", type); 801ca3176e7SBrian Feldman error("The fingerprint for the %s key sent by the remote host is\n%s.", 802ca3176e7SBrian Feldman type, fp); 803e8aafc91SKris Kennaway error("Please contact your system administrator."); 804e8aafc91SKris Kennaway error("Add correct host key in %.100s to get rid of this message.", 805e8aafc91SKris Kennaway user_hostfile); 806ca3176e7SBrian Feldman error("Offending key in %s:%d", host_file, host_line); 807ca3176e7SBrian Feldman xfree(fp); 808e8aafc91SKris Kennaway 809e8aafc91SKris Kennaway /* 810e8aafc91SKris Kennaway * If strict host key checking is in use, the user will have 811e8aafc91SKris Kennaway * to edit the key manually and we can only abort. 812e8aafc91SKris Kennaway */ 813af12a3e7SDag-Erling Smørgrav if (options.strict_host_key_checking) { 814af12a3e7SDag-Erling Smørgrav error("%s host key for %.200s has changed and you have " 815af12a3e7SDag-Erling Smørgrav "requested strict checking.", type, host); 816af12a3e7SDag-Erling Smørgrav goto fail; 817af12a3e7SDag-Erling Smørgrav } 818e8aafc91SKris Kennaway 819e8aafc91SKris Kennaway /* 820e8aafc91SKris Kennaway * If strict host key checking has not been requested, allow 821cf2b5f3bSDag-Erling Smørgrav * the connection but without MITM-able authentication or 822e8aafc91SKris Kennaway * agent forwarding. 823e8aafc91SKris Kennaway */ 824e8aafc91SKris Kennaway if (options.password_authentication) { 825af12a3e7SDag-Erling Smørgrav error("Password authentication is disabled to avoid " 826af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 827e8aafc91SKris Kennaway options.password_authentication = 0; 828e8aafc91SKris Kennaway } 829cf2b5f3bSDag-Erling Smørgrav if (options.kbd_interactive_authentication) { 830cf2b5f3bSDag-Erling Smørgrav error("Keyboard-interactive authentication is disabled" 831cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 832cf2b5f3bSDag-Erling Smørgrav options.kbd_interactive_authentication = 0; 833cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 834cf2b5f3bSDag-Erling Smørgrav } 835cf2b5f3bSDag-Erling Smørgrav if (options.challenge_response_authentication) { 836cf2b5f3bSDag-Erling Smørgrav error("Challenge/response authentication is disabled" 837cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 838cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 839cf2b5f3bSDag-Erling Smørgrav } 840e8aafc91SKris Kennaway if (options.forward_agent) { 841af12a3e7SDag-Erling Smørgrav error("Agent forwarding is disabled to avoid " 842af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 843e8aafc91SKris Kennaway options.forward_agent = 0; 844e8aafc91SKris Kennaway } 845ca3176e7SBrian Feldman if (options.forward_x11) { 846af12a3e7SDag-Erling Smørgrav error("X11 forwarding is disabled to avoid " 847af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 848ca3176e7SBrian Feldman options.forward_x11 = 0; 849ca3176e7SBrian Feldman } 850af12a3e7SDag-Erling Smørgrav if (options.num_local_forwards > 0 || 851af12a3e7SDag-Erling Smørgrav options.num_remote_forwards > 0) { 852af12a3e7SDag-Erling Smørgrav error("Port forwarding is disabled to avoid " 853af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 854af12a3e7SDag-Erling Smørgrav options.num_local_forwards = 855af12a3e7SDag-Erling Smørgrav options.num_remote_forwards = 0; 856ca3176e7SBrian Feldman } 857e8aafc91SKris Kennaway /* 858e8aafc91SKris Kennaway * XXX Should permit the user to change to use the new id. 859e8aafc91SKris Kennaway * This could be done by converting the host key to an 860e8aafc91SKris Kennaway * identifying sentence, tell that the host identifies itself 861e8aafc91SKris Kennaway * by that sentence, and ask the user if he/she whishes to 862e8aafc91SKris Kennaway * accept the authentication. 863e8aafc91SKris Kennaway */ 864e8aafc91SKris Kennaway break; 865f388f5efSDag-Erling Smørgrav case HOST_FOUND: 866f388f5efSDag-Erling Smørgrav fatal("internal error"); 867f388f5efSDag-Erling Smørgrav break; 868e8aafc91SKris Kennaway } 869ca3176e7SBrian Feldman 870ca3176e7SBrian Feldman if (options.check_host_ip && host_status != HOST_CHANGED && 871ca3176e7SBrian Feldman ip_status == HOST_CHANGED) { 872af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 873af12a3e7SDag-Erling Smørgrav "Warning: the %s host key for '%.200s' " 874af12a3e7SDag-Erling Smørgrav "differs from the key for the IP address '%.128s'" 875af12a3e7SDag-Erling Smørgrav "\nOffending key for IP in %s:%d", 876af12a3e7SDag-Erling Smørgrav type, host, ip, ip_file, ip_line); 877af12a3e7SDag-Erling Smørgrav if (host_status == HOST_OK) { 878af12a3e7SDag-Erling Smørgrav len = strlen(msg); 879af12a3e7SDag-Erling Smørgrav snprintf(msg + len, sizeof(msg) - len, 880af12a3e7SDag-Erling Smørgrav "\nMatching host key in %s:%d", 881af12a3e7SDag-Erling Smørgrav host_file, host_line); 882af12a3e7SDag-Erling Smørgrav } 883ca3176e7SBrian Feldman if (options.strict_host_key_checking == 1) { 884cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 885af12a3e7SDag-Erling Smørgrav error("Exiting, you have requested strict checking."); 886af12a3e7SDag-Erling Smørgrav goto fail; 887ca3176e7SBrian Feldman } else if (options.strict_host_key_checking == 2) { 888af12a3e7SDag-Erling Smørgrav strlcat(msg, "\nAre you sure you want " 889af12a3e7SDag-Erling Smørgrav "to continue connecting (yes/no)? ", sizeof(msg)); 890af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 891af12a3e7SDag-Erling Smørgrav goto fail; 892af12a3e7SDag-Erling Smørgrav } else { 893cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 894ca3176e7SBrian Feldman } 895ca3176e7SBrian Feldman } 896ca3176e7SBrian Feldman 897e8aafc91SKris Kennaway xfree(ip); 898af12a3e7SDag-Erling Smørgrav return 0; 899af12a3e7SDag-Erling Smørgrav 900af12a3e7SDag-Erling Smørgrav fail: 901af12a3e7SDag-Erling Smørgrav xfree(ip); 902af12a3e7SDag-Erling Smørgrav return -1; 903e8aafc91SKris Kennaway } 904511b41d2SMark Murray 905cf2b5f3bSDag-Erling Smørgrav /* returns 0 if key verifies or -1 if key does NOT verify */ 906fe5fd017SMark Murray int 907af12a3e7SDag-Erling Smørgrav verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 908fe5fd017SMark Murray { 909af12a3e7SDag-Erling Smørgrav struct stat st; 910fe5fd017SMark Murray 911cf2b5f3bSDag-Erling Smørgrav #ifdef DNS 912cf2b5f3bSDag-Erling Smørgrav if (options.verify_host_key_dns) { 913cf2b5f3bSDag-Erling Smørgrav switch(verify_host_key_dns(host, hostaddr, host_key)) { 914cf2b5f3bSDag-Erling Smørgrav case DNS_VERIFY_OK: 915cf2b5f3bSDag-Erling Smørgrav #ifdef DNSSEC 916cf2b5f3bSDag-Erling Smørgrav return 0; 917cf2b5f3bSDag-Erling Smørgrav #else 918cf2b5f3bSDag-Erling Smørgrav verified_host_key_dns = 1; 919cf2b5f3bSDag-Erling Smørgrav break; 920cf2b5f3bSDag-Erling Smørgrav #endif 921cf2b5f3bSDag-Erling Smørgrav case DNS_VERIFY_FAILED: 922cf2b5f3bSDag-Erling Smørgrav return -1; 923cf2b5f3bSDag-Erling Smørgrav case DNS_VERIFY_ERROR: 924cf2b5f3bSDag-Erling Smørgrav break; 925cf2b5f3bSDag-Erling Smørgrav default: 926cf2b5f3bSDag-Erling Smørgrav debug3("bad return value from verify_host_key_dns"); 927cf2b5f3bSDag-Erling Smørgrav break; 928cf2b5f3bSDag-Erling Smørgrav } 929cf2b5f3bSDag-Erling Smørgrav } 930cf2b5f3bSDag-Erling Smørgrav #endif /* DNS */ 931cf2b5f3bSDag-Erling Smørgrav 932af12a3e7SDag-Erling Smørgrav /* return ok if the key can be found in an old keyfile */ 933af12a3e7SDag-Erling Smørgrav if (stat(options.system_hostfile2, &st) == 0 || 934af12a3e7SDag-Erling Smørgrav stat(options.user_hostfile2, &st) == 0) { 935af12a3e7SDag-Erling Smørgrav if (check_host_key(host, hostaddr, host_key, /*readonly*/ 1, 936af12a3e7SDag-Erling Smørgrav options.user_hostfile2, options.system_hostfile2) == 0) 937af12a3e7SDag-Erling Smørgrav return 0; 938fe5fd017SMark Murray } 939af12a3e7SDag-Erling Smørgrav return check_host_key(host, hostaddr, host_key, /*readonly*/ 0, 940af12a3e7SDag-Erling Smørgrav options.user_hostfile, options.system_hostfile); 941fe5fd017SMark Murray } 942fe5fd017SMark Murray 943511b41d2SMark Murray /* 944511b41d2SMark Murray * Starts a dialog with the server, and authenticates the current user on the 945511b41d2SMark Murray * server. This does not need any extra privileges. The basic connection 946511b41d2SMark Murray * to the server must already have been established before this is called. 947511b41d2SMark Murray * If login fails, this function prints an error and never returns. 948511b41d2SMark Murray * This function does not require super-user privileges. 949511b41d2SMark Murray */ 950511b41d2SMark Murray void 95180628bacSDag-Erling Smørgrav ssh_login(Sensitive *sensitive, const char *orighost, 952ca3176e7SBrian Feldman struct sockaddr *hostaddr, struct passwd *pw) 953511b41d2SMark Murray { 954511b41d2SMark Murray char *host, *cp; 955e8aafc91SKris Kennaway char *server_user, *local_user; 956e8aafc91SKris Kennaway 957e8aafc91SKris Kennaway local_user = xstrdup(pw->pw_name); 958e8aafc91SKris Kennaway server_user = options.user ? options.user : local_user; 959511b41d2SMark Murray 960511b41d2SMark Murray /* Convert the user-supplied hostname into all lowercase. */ 961511b41d2SMark Murray host = xstrdup(orighost); 962511b41d2SMark Murray for (cp = host; *cp; cp++) 963511b41d2SMark Murray if (isupper(*cp)) 964511b41d2SMark Murray *cp = tolower(*cp); 965511b41d2SMark Murray 966511b41d2SMark Murray /* Exchange protocol version identification strings with the server. */ 967511b41d2SMark Murray ssh_exchange_identification(); 968511b41d2SMark Murray 969511b41d2SMark Murray /* Put the connection into non-blocking mode. */ 970511b41d2SMark Murray packet_set_nonblocking(); 971511b41d2SMark Murray 972511b41d2SMark Murray /* key exchange */ 973511b41d2SMark Murray /* authenticate user */ 974e8aafc91SKris Kennaway if (compat20) { 975e8aafc91SKris Kennaway ssh_kex2(host, hostaddr); 97680628bacSDag-Erling Smørgrav ssh_userauth2(local_user, server_user, host, sensitive); 977e8aafc91SKris Kennaway } else { 978e8aafc91SKris Kennaway ssh_kex(host, hostaddr); 97980628bacSDag-Erling Smørgrav ssh_userauth1(local_user, server_user, host, sensitive); 980e8aafc91SKris Kennaway } 981511b41d2SMark Murray } 982e0fbb1d2SBrian Feldman 983e0fbb1d2SBrian Feldman void 984e0fbb1d2SBrian Feldman ssh_put_password(char *password) 985e0fbb1d2SBrian Feldman { 986e0fbb1d2SBrian Feldman int size; 987e0fbb1d2SBrian Feldman char *padded; 988e0fbb1d2SBrian Feldman 989ca3176e7SBrian Feldman if (datafellows & SSH_BUG_PASSWORDPAD) { 990af12a3e7SDag-Erling Smørgrav packet_put_cstring(password); 991ca3176e7SBrian Feldman return; 992ca3176e7SBrian Feldman } 993e0fbb1d2SBrian Feldman size = roundup(strlen(password) + 1, 32); 994e0fbb1d2SBrian Feldman padded = xmalloc(size); 995e0fbb1d2SBrian Feldman memset(padded, 0, size); 996e0fbb1d2SBrian Feldman strlcpy(padded, password, size); 997e0fbb1d2SBrian Feldman packet_put_string(padded, size); 998e0fbb1d2SBrian Feldman memset(padded, 0, size); 999e0fbb1d2SBrian Feldman xfree(padded); 1000e0fbb1d2SBrian Feldman } 1001f388f5efSDag-Erling Smørgrav 1002f388f5efSDag-Erling Smørgrav static int 1003f388f5efSDag-Erling Smørgrav show_key_from_file(const char *file, const char *host, int keytype) 1004f388f5efSDag-Erling Smørgrav { 1005f388f5efSDag-Erling Smørgrav Key *found; 1006f388f5efSDag-Erling Smørgrav char *fp; 1007f388f5efSDag-Erling Smørgrav int line, ret; 1008f388f5efSDag-Erling Smørgrav 1009f388f5efSDag-Erling Smørgrav found = key_new(keytype); 1010f388f5efSDag-Erling Smørgrav if ((ret = lookup_key_in_hostfile_by_type(file, host, 1011f388f5efSDag-Erling Smørgrav keytype, found, &line))) { 1012f388f5efSDag-Erling Smørgrav fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); 1013cf2b5f3bSDag-Erling Smørgrav logit("WARNING: %s key found for host %s\n" 1014f388f5efSDag-Erling Smørgrav "in %s:%d\n" 1015f388f5efSDag-Erling Smørgrav "%s key fingerprint %s.", 1016f388f5efSDag-Erling Smørgrav key_type(found), host, file, line, 1017f388f5efSDag-Erling Smørgrav key_type(found), fp); 1018f388f5efSDag-Erling Smørgrav xfree(fp); 1019f388f5efSDag-Erling Smørgrav } 1020f388f5efSDag-Erling Smørgrav key_free(found); 1021f388f5efSDag-Erling Smørgrav return (ret); 1022f388f5efSDag-Erling Smørgrav } 1023f388f5efSDag-Erling Smørgrav 1024f388f5efSDag-Erling Smørgrav /* print all known host keys for a given host, but skip keys of given type */ 1025f388f5efSDag-Erling Smørgrav static int 1026f388f5efSDag-Erling Smørgrav show_other_keys(const char *host, Key *key) 1027f388f5efSDag-Erling Smørgrav { 1028f388f5efSDag-Erling Smørgrav int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, -1}; 1029f388f5efSDag-Erling Smørgrav int i, found = 0; 1030f388f5efSDag-Erling Smørgrav 1031f388f5efSDag-Erling Smørgrav for (i = 0; type[i] != -1; i++) { 1032f388f5efSDag-Erling Smørgrav if (type[i] == key->type) 1033f388f5efSDag-Erling Smørgrav continue; 1034f388f5efSDag-Erling Smørgrav if (type[i] != KEY_RSA1 && 1035f388f5efSDag-Erling Smørgrav show_key_from_file(options.user_hostfile2, host, type[i])) { 1036f388f5efSDag-Erling Smørgrav found = 1; 1037f388f5efSDag-Erling Smørgrav continue; 1038f388f5efSDag-Erling Smørgrav } 1039f388f5efSDag-Erling Smørgrav if (type[i] != KEY_RSA1 && 1040f388f5efSDag-Erling Smørgrav show_key_from_file(options.system_hostfile2, host, type[i])) { 1041f388f5efSDag-Erling Smørgrav found = 1; 1042f388f5efSDag-Erling Smørgrav continue; 1043f388f5efSDag-Erling Smørgrav } 1044f388f5efSDag-Erling Smørgrav if (show_key_from_file(options.user_hostfile, host, type[i])) { 1045f388f5efSDag-Erling Smørgrav found = 1; 1046f388f5efSDag-Erling Smørgrav continue; 1047f388f5efSDag-Erling Smørgrav } 1048f388f5efSDag-Erling Smørgrav if (show_key_from_file(options.system_hostfile, host, type[i])) { 1049f388f5efSDag-Erling Smørgrav found = 1; 1050f388f5efSDag-Erling Smørgrav continue; 1051f388f5efSDag-Erling Smørgrav } 1052f388f5efSDag-Erling Smørgrav debug2("no key of type %d for host %s", type[i], host); 1053f388f5efSDag-Erling Smørgrav } 1054f388f5efSDag-Erling Smørgrav return (found); 1055f388f5efSDag-Erling Smørgrav } 1056