16888a9beSDag-Erling Smørgrav /* $OpenBSD: sshconnect.c,v 1.236 2012/09/14 16:51:34 markus Exp $ */ 2*420bce64SDag-Erling Smørgrav /* $OpenBSD: sshconnect.c,v 1.237 2013/02/22 19:13:56 markus Exp $ */ 3511b41d2SMark Murray /* 4511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 5511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 6511b41d2SMark Murray * All rights reserved 7511b41d2SMark Murray * Code to connect to a remote host, and to perform the client side of the 8511b41d2SMark Murray * login (authentication) dialog. 942f71286SMark Murray * 10c2d3a559SKris Kennaway * As far as I am concerned, the code I have written for this software 11c2d3a559SKris Kennaway * can be used freely for any purpose. Any derived versions of this 12c2d3a559SKris Kennaway * software must be clearly marked as such, and if the derived work is 13c2d3a559SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 14c2d3a559SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 15511b41d2SMark Murray */ 16511b41d2SMark Murray 17511b41d2SMark Murray #include "includes.h" 18511b41d2SMark Murray 19333ee039SDag-Erling Smørgrav #include <sys/types.h> 20333ee039SDag-Erling Smørgrav #include <sys/wait.h> 21333ee039SDag-Erling Smørgrav #include <sys/stat.h> 22333ee039SDag-Erling Smørgrav #include <sys/socket.h> 23333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 24333ee039SDag-Erling Smørgrav # include <sys/time.h> 25333ee039SDag-Erling Smørgrav #endif 26e8aafc91SKris Kennaway 27333ee039SDag-Erling Smørgrav #include <netinet/in.h> 28333ee039SDag-Erling Smørgrav #include <arpa/inet.h> 29333ee039SDag-Erling Smørgrav 30333ee039SDag-Erling Smørgrav #include <ctype.h> 31333ee039SDag-Erling Smørgrav #include <errno.h> 32b15c8340SDag-Erling Smørgrav #include <fcntl.h> 33333ee039SDag-Erling Smørgrav #include <netdb.h> 34333ee039SDag-Erling Smørgrav #ifdef HAVE_PATHS_H 35333ee039SDag-Erling Smørgrav #include <paths.h> 36333ee039SDag-Erling Smørgrav #endif 37333ee039SDag-Erling Smørgrav #include <pwd.h> 384a421b63SDag-Erling Smørgrav #include <signal.h> 39333ee039SDag-Erling Smørgrav #include <stdarg.h> 40333ee039SDag-Erling Smørgrav #include <stdio.h> 41333ee039SDag-Erling Smørgrav #include <stdlib.h> 42333ee039SDag-Erling Smørgrav #include <string.h> 43333ee039SDag-Erling Smørgrav #include <unistd.h> 44333ee039SDag-Erling Smørgrav 45511b41d2SMark Murray #include "xmalloc.h" 46333ee039SDag-Erling Smørgrav #include "key.h" 47333ee039SDag-Erling Smørgrav #include "hostfile.h" 48333ee039SDag-Erling Smørgrav #include "ssh.h" 49511b41d2SMark Murray #include "rsa.h" 50e8aafc91SKris Kennaway #include "buffer.h" 51511b41d2SMark Murray #include "packet.h" 52511b41d2SMark Murray #include "uidswap.h" 53511b41d2SMark Murray #include "compat.h" 543c6ae118SKris Kennaway #include "key.h" 55e8aafc91SKris Kennaway #include "sshconnect.h" 563c6ae118SKris Kennaway #include "hostfile.h" 57ca3176e7SBrian Feldman #include "log.h" 58ca3176e7SBrian Feldman #include "readconf.h" 59ca3176e7SBrian Feldman #include "atomicio.h" 60ca3176e7SBrian Feldman #include "misc.h" 61cf2b5f3bSDag-Erling Smørgrav #include "dns.h" 627aee6ffeSDag-Erling Smørgrav #include "roaming.h" 63b15c8340SDag-Erling Smørgrav #include "ssh2.h" 64333ee039SDag-Erling Smørgrav #include "version.h" 65cf2b5f3bSDag-Erling Smørgrav 66e8aafc91SKris Kennaway char *client_version_string = NULL; 67e8aafc91SKris Kennaway char *server_version_string = NULL; 68511b41d2SMark Murray 69b74df5b2SDag-Erling Smørgrav static int matching_host_key_dns = 0; 70cf2b5f3bSDag-Erling Smørgrav 714a421b63SDag-Erling Smørgrav static pid_t proxy_command_pid = 0; 724a421b63SDag-Erling Smørgrav 7380628bacSDag-Erling Smørgrav /* import */ 74511b41d2SMark Murray extern Options options; 75511b41d2SMark Murray extern char *__progname; 7680628bacSDag-Erling Smørgrav extern uid_t original_real_uid; 7780628bacSDag-Erling Smørgrav extern uid_t original_effective_uid; 78511b41d2SMark Murray 794a421b63SDag-Erling Smørgrav static int show_other_keys(struct hostkeys *, Key *); 801ec0d754SDag-Erling Smørgrav static void warn_changed_key(Key *); 81ca3176e7SBrian Feldman 82511b41d2SMark Murray /* 83511b41d2SMark Murray * Connect to the given ssh server using a proxy command. 84511b41d2SMark Murray */ 85af12a3e7SDag-Erling Smørgrav static int 8680628bacSDag-Erling Smørgrav ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) 87511b41d2SMark Murray { 88d4ecd108SDag-Erling Smørgrav char *command_string, *tmp; 89511b41d2SMark Murray int pin[2], pout[2]; 90e8aafc91SKris Kennaway pid_t pid; 91d4af9e69SDag-Erling Smørgrav char *shell, strport[NI_MAXSERV]; 92d4af9e69SDag-Erling Smørgrav 93*420bce64SDag-Erling Smørgrav if (!strcmp(proxy_command, "-")) { 94*420bce64SDag-Erling Smørgrav packet_set_connection(STDIN_FILENO, STDOUT_FILENO); 95*420bce64SDag-Erling Smørgrav packet_set_timeout(options.server_alive_interval, 96*420bce64SDag-Erling Smørgrav options.server_alive_count_max); 97*420bce64SDag-Erling Smørgrav return 0; 98*420bce64SDag-Erling Smørgrav } 99*420bce64SDag-Erling Smørgrav 1004a421b63SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 101d4af9e69SDag-Erling Smørgrav shell = _PATH_BSHELL; 102511b41d2SMark Murray 103511b41d2SMark Murray /* Convert the port number into a string. */ 104511b41d2SMark Murray snprintf(strport, sizeof strport, "%hu", port); 105511b41d2SMark Murray 106f388f5efSDag-Erling Smørgrav /* 107f388f5efSDag-Erling Smørgrav * Build the final command string in the buffer by making the 108f388f5efSDag-Erling Smørgrav * appropriate substitutions to the given proxy command. 109f388f5efSDag-Erling Smørgrav * 110f388f5efSDag-Erling Smørgrav * Use "exec" to avoid "sh -c" processes on some platforms 111f388f5efSDag-Erling Smørgrav * (e.g. Solaris) 112f388f5efSDag-Erling Smørgrav */ 113333ee039SDag-Erling Smørgrav xasprintf(&tmp, "exec %s", proxy_command); 114e2f6069cSDag-Erling Smørgrav command_string = percent_expand(tmp, "h", host, "p", strport, 115e2f6069cSDag-Erling Smørgrav "r", options.user, (char *)NULL); 116d4ecd108SDag-Erling Smørgrav xfree(tmp); 117511b41d2SMark Murray 118511b41d2SMark Murray /* Create pipes for communicating with the proxy. */ 119511b41d2SMark Murray if (pipe(pin) < 0 || pipe(pout) < 0) 120511b41d2SMark Murray fatal("Could not create pipes to communicate with the proxy: %.100s", 121511b41d2SMark Murray strerror(errno)); 122511b41d2SMark Murray 123511b41d2SMark Murray debug("Executing proxy command: %.500s", command_string); 124511b41d2SMark Murray 125511b41d2SMark Murray /* Fork and execute the proxy command. */ 126511b41d2SMark Murray if ((pid = fork()) == 0) { 127511b41d2SMark Murray char *argv[10]; 128511b41d2SMark Murray 129511b41d2SMark Murray /* Child. Permanently give up superuser privileges. */ 130333ee039SDag-Erling Smørgrav permanently_drop_suid(original_real_uid); 131511b41d2SMark Murray 132511b41d2SMark Murray /* Redirect stdin and stdout. */ 133511b41d2SMark Murray close(pin[1]); 134511b41d2SMark Murray if (pin[0] != 0) { 135511b41d2SMark Murray if (dup2(pin[0], 0) < 0) 136511b41d2SMark Murray perror("dup2 stdin"); 137511b41d2SMark Murray close(pin[0]); 138511b41d2SMark Murray } 139511b41d2SMark Murray close(pout[0]); 140511b41d2SMark Murray if (dup2(pout[1], 1) < 0) 141511b41d2SMark Murray perror("dup2 stdout"); 142511b41d2SMark Murray /* Cannot be 1 because pin allocated two descriptors. */ 143511b41d2SMark Murray close(pout[1]); 144511b41d2SMark Murray 145511b41d2SMark Murray /* Stderr is left as it is so that error messages get 146511b41d2SMark Murray printed on the user's terminal. */ 147d4af9e69SDag-Erling Smørgrav argv[0] = shell; 148511b41d2SMark Murray argv[1] = "-c"; 149511b41d2SMark Murray argv[2] = command_string; 150511b41d2SMark Murray argv[3] = NULL; 151511b41d2SMark Murray 152511b41d2SMark Murray /* Execute the proxy command. Note that we gave up any 153511b41d2SMark Murray extra privileges above. */ 1544a421b63SDag-Erling Smørgrav signal(SIGPIPE, SIG_DFL); 155ca3176e7SBrian Feldman execv(argv[0], argv); 156ca3176e7SBrian Feldman perror(argv[0]); 157511b41d2SMark Murray exit(1); 158511b41d2SMark Murray } 159511b41d2SMark Murray /* Parent. */ 160511b41d2SMark Murray if (pid < 0) 161511b41d2SMark Murray fatal("fork failed: %.100s", strerror(errno)); 162f388f5efSDag-Erling Smørgrav else 163f388f5efSDag-Erling Smørgrav proxy_command_pid = pid; /* save pid to clean up later */ 164511b41d2SMark Murray 165511b41d2SMark Murray /* Close child side of the descriptors. */ 166511b41d2SMark Murray close(pin[0]); 167511b41d2SMark Murray close(pout[1]); 168511b41d2SMark Murray 169511b41d2SMark Murray /* Free the command name. */ 170d4ecd108SDag-Erling Smørgrav xfree(command_string); 171511b41d2SMark Murray 172511b41d2SMark Murray /* Set the connection file descriptors. */ 173511b41d2SMark Murray packet_set_connection(pout[0], pin[1]); 174d4af9e69SDag-Erling Smørgrav packet_set_timeout(options.server_alive_interval, 175d4af9e69SDag-Erling Smørgrav options.server_alive_count_max); 176511b41d2SMark Murray 177af12a3e7SDag-Erling Smørgrav /* Indicate OK return */ 178af12a3e7SDag-Erling Smørgrav return 0; 179511b41d2SMark Murray } 180511b41d2SMark Murray 1814a421b63SDag-Erling Smørgrav void 1824a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(void) 1834a421b63SDag-Erling Smørgrav { 1844a421b63SDag-Erling Smørgrav /* 1854a421b63SDag-Erling Smørgrav * Send SIGHUP to proxy command if used. We don't wait() in 1864a421b63SDag-Erling Smørgrav * case it hangs and instead rely on init to reap the child 1874a421b63SDag-Erling Smørgrav */ 1884a421b63SDag-Erling Smørgrav if (proxy_command_pid > 1) 1894a421b63SDag-Erling Smørgrav kill(proxy_command_pid, SIGHUP); 1904a421b63SDag-Erling Smørgrav } 1914a421b63SDag-Erling Smørgrav 192511b41d2SMark Murray /* 19389986192SBrooks Davis * Set TCP receive buffer if requested. 19489986192SBrooks Davis * Note: tuning needs to happen after the socket is created but before the 19589986192SBrooks Davis * connection happens so winscale is negotiated properly. 19689986192SBrooks Davis */ 19789986192SBrooks Davis static void 19889986192SBrooks Davis ssh_set_socket_recvbuf(int sock) 19989986192SBrooks Davis { 20089986192SBrooks Davis void *buf = (void *)&options.tcp_rcv_buf; 20189986192SBrooks Davis int socksize, sz = sizeof(options.tcp_rcv_buf); 20289986192SBrooks Davis socklen_t len = sizeof(int); 20389986192SBrooks Davis 20489986192SBrooks Davis debug("setsockopt attempting to set SO_RCVBUF to %d", 20589986192SBrooks Davis options.tcp_rcv_buf); 20689986192SBrooks Davis if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, buf, sz) >= 0) { 20789986192SBrooks Davis getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &socksize, &len); 20889986192SBrooks Davis debug("setsockopt SO_RCVBUF: %.100s %d", strerror(errno), 20989986192SBrooks Davis socksize); 21089986192SBrooks Davis } else 21189986192SBrooks Davis error("Couldn't set socket receive buffer to %d: %.100s", 21289986192SBrooks Davis options.tcp_rcv_buf, strerror(errno)); 21389986192SBrooks Davis } 21489986192SBrooks Davis 21589986192SBrooks Davis /* 216511b41d2SMark Murray * Creates a (possibly privileged) socket for use as the ssh connection. 217511b41d2SMark Murray */ 218af12a3e7SDag-Erling Smørgrav static int 219cf2b5f3bSDag-Erling Smørgrav ssh_create_socket(int privileged, struct addrinfo *ai) 220511b41d2SMark Murray { 221af12a3e7SDag-Erling Smørgrav int sock, gaierr; 222af12a3e7SDag-Erling Smørgrav struct addrinfo hints, *res; 223511b41d2SMark Murray 224511b41d2SMark Murray /* 225511b41d2SMark Murray * If we are running as root and want to connect to a privileged 226511b41d2SMark Murray * port, bind our own socket to a privileged port. 227511b41d2SMark Murray */ 228511b41d2SMark Murray if (privileged) { 229511b41d2SMark Murray int p = IPPORT_RESERVED - 1; 23080628bacSDag-Erling Smørgrav PRIV_START; 231cf2b5f3bSDag-Erling Smørgrav sock = rresvport_af(&p, ai->ai_family); 23280628bacSDag-Erling Smørgrav PRIV_END; 233511b41d2SMark Murray if (sock < 0) 234cf2b5f3bSDag-Erling Smørgrav error("rresvport: af=%d %.100s", ai->ai_family, 235cf2b5f3bSDag-Erling Smørgrav strerror(errno)); 236511b41d2SMark Murray else 237511b41d2SMark Murray debug("Allocated local port %d.", p); 23889986192SBrooks Davis if (options.tcp_rcv_buf > 0) 23989986192SBrooks Davis ssh_set_socket_recvbuf(sock); 240af12a3e7SDag-Erling Smørgrav return sock; 241af12a3e7SDag-Erling Smørgrav } 242cf2b5f3bSDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 243b15c8340SDag-Erling Smørgrav if (sock < 0) { 244511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 245b15c8340SDag-Erling Smørgrav return -1; 246b15c8340SDag-Erling Smørgrav } 247b15c8340SDag-Erling Smørgrav fcntl(sock, F_SETFD, FD_CLOEXEC); 248af12a3e7SDag-Erling Smørgrav 24989986192SBrooks Davis if (options.tcp_rcv_buf > 0) 25089986192SBrooks Davis ssh_set_socket_recvbuf(sock); 25189986192SBrooks Davis 252af12a3e7SDag-Erling Smørgrav /* Bind the socket to an alternative local IP address */ 253af12a3e7SDag-Erling Smørgrav if (options.bind_address == NULL) 254af12a3e7SDag-Erling Smørgrav return sock; 255af12a3e7SDag-Erling Smørgrav 256af12a3e7SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 257cf2b5f3bSDag-Erling Smørgrav hints.ai_family = ai->ai_family; 258cf2b5f3bSDag-Erling Smørgrav hints.ai_socktype = ai->ai_socktype; 259cf2b5f3bSDag-Erling Smørgrav hints.ai_protocol = ai->ai_protocol; 260af12a3e7SDag-Erling Smørgrav hints.ai_flags = AI_PASSIVE; 261d4af9e69SDag-Erling Smørgrav gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); 262af12a3e7SDag-Erling Smørgrav if (gaierr) { 263af12a3e7SDag-Erling Smørgrav error("getaddrinfo: %s: %s", options.bind_address, 264d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 265af12a3e7SDag-Erling Smørgrav close(sock); 266af12a3e7SDag-Erling Smørgrav return -1; 267511b41d2SMark Murray } 268af12a3e7SDag-Erling Smørgrav if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 269af12a3e7SDag-Erling Smørgrav error("bind: %s: %s", options.bind_address, strerror(errno)); 270af12a3e7SDag-Erling Smørgrav close(sock); 271af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 272af12a3e7SDag-Erling Smørgrav return -1; 273af12a3e7SDag-Erling Smørgrav } 274af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 275511b41d2SMark Murray return sock; 276511b41d2SMark Murray } 277511b41d2SMark Murray 278cf2b5f3bSDag-Erling Smørgrav static int 279cf2b5f3bSDag-Erling Smørgrav timeout_connect(int sockfd, const struct sockaddr *serv_addr, 280d4af9e69SDag-Erling Smørgrav socklen_t addrlen, int *timeoutp) 281cf2b5f3bSDag-Erling Smørgrav { 282cf2b5f3bSDag-Erling Smørgrav fd_set *fdset; 283d4af9e69SDag-Erling Smørgrav struct timeval tv, t_start; 284cf2b5f3bSDag-Erling Smørgrav socklen_t optlen; 285333ee039SDag-Erling Smørgrav int optval, rc, result = -1; 286cf2b5f3bSDag-Erling Smørgrav 287d4af9e69SDag-Erling Smørgrav gettimeofday(&t_start, NULL); 288d4af9e69SDag-Erling Smørgrav 289d4af9e69SDag-Erling Smørgrav if (*timeoutp <= 0) { 290d4af9e69SDag-Erling Smørgrav result = connect(sockfd, serv_addr, addrlen); 291d4af9e69SDag-Erling Smørgrav goto done; 292d4af9e69SDag-Erling Smørgrav } 293cf2b5f3bSDag-Erling Smørgrav 2941ec0d754SDag-Erling Smørgrav set_nonblock(sockfd); 295cf2b5f3bSDag-Erling Smørgrav rc = connect(sockfd, serv_addr, addrlen); 2961ec0d754SDag-Erling Smørgrav if (rc == 0) { 2971ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 298d4af9e69SDag-Erling Smørgrav result = 0; 299d4af9e69SDag-Erling Smørgrav goto done; 3001ec0d754SDag-Erling Smørgrav } 301d4af9e69SDag-Erling Smørgrav if (errno != EINPROGRESS) { 302d4af9e69SDag-Erling Smørgrav result = -1; 303d4af9e69SDag-Erling Smørgrav goto done; 304d4af9e69SDag-Erling Smørgrav } 305cf2b5f3bSDag-Erling Smørgrav 306333ee039SDag-Erling Smørgrav fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), 307333ee039SDag-Erling Smørgrav sizeof(fd_mask)); 308cf2b5f3bSDag-Erling Smørgrav FD_SET(sockfd, fdset); 309d4af9e69SDag-Erling Smørgrav ms_to_timeval(&tv, *timeoutp); 310cf2b5f3bSDag-Erling Smørgrav 311cf2b5f3bSDag-Erling Smørgrav for (;;) { 312cf2b5f3bSDag-Erling Smørgrav rc = select(sockfd + 1, NULL, fdset, NULL, &tv); 313cf2b5f3bSDag-Erling Smørgrav if (rc != -1 || errno != EINTR) 314cf2b5f3bSDag-Erling Smørgrav break; 315cf2b5f3bSDag-Erling Smørgrav } 316cf2b5f3bSDag-Erling Smørgrav 317cf2b5f3bSDag-Erling Smørgrav switch (rc) { 318cf2b5f3bSDag-Erling Smørgrav case 0: 319cf2b5f3bSDag-Erling Smørgrav /* Timed out */ 320cf2b5f3bSDag-Erling Smørgrav errno = ETIMEDOUT; 321cf2b5f3bSDag-Erling Smørgrav break; 322cf2b5f3bSDag-Erling Smørgrav case -1: 323cf2b5f3bSDag-Erling Smørgrav /* Select error */ 324cf2b5f3bSDag-Erling Smørgrav debug("select: %s", strerror(errno)); 325cf2b5f3bSDag-Erling Smørgrav break; 326cf2b5f3bSDag-Erling Smørgrav case 1: 327cf2b5f3bSDag-Erling Smørgrav /* Completed or failed */ 328cf2b5f3bSDag-Erling Smørgrav optval = 0; 329cf2b5f3bSDag-Erling Smørgrav optlen = sizeof(optval); 330cf2b5f3bSDag-Erling Smørgrav if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, 331cf2b5f3bSDag-Erling Smørgrav &optlen) == -1) { 332cf2b5f3bSDag-Erling Smørgrav debug("getsockopt: %s", strerror(errno)); 333cf2b5f3bSDag-Erling Smørgrav break; 334cf2b5f3bSDag-Erling Smørgrav } 335cf2b5f3bSDag-Erling Smørgrav if (optval != 0) { 336cf2b5f3bSDag-Erling Smørgrav errno = optval; 337cf2b5f3bSDag-Erling Smørgrav break; 338cf2b5f3bSDag-Erling Smørgrav } 339cf2b5f3bSDag-Erling Smørgrav result = 0; 3401ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 341cf2b5f3bSDag-Erling Smørgrav break; 342cf2b5f3bSDag-Erling Smørgrav default: 343cf2b5f3bSDag-Erling Smørgrav /* Should not occur */ 344cf2b5f3bSDag-Erling Smørgrav fatal("Bogus return (%d) from select()", rc); 345cf2b5f3bSDag-Erling Smørgrav } 346cf2b5f3bSDag-Erling Smørgrav 347cf2b5f3bSDag-Erling Smørgrav xfree(fdset); 348d4af9e69SDag-Erling Smørgrav 349d4af9e69SDag-Erling Smørgrav done: 350d4af9e69SDag-Erling Smørgrav if (result == 0 && *timeoutp > 0) { 351d4af9e69SDag-Erling Smørgrav ms_subtract_diff(&t_start, timeoutp); 352d4af9e69SDag-Erling Smørgrav if (*timeoutp <= 0) { 353d4af9e69SDag-Erling Smørgrav errno = ETIMEDOUT; 354d4af9e69SDag-Erling Smørgrav result = -1; 355d4af9e69SDag-Erling Smørgrav } 356d4af9e69SDag-Erling Smørgrav } 357d4af9e69SDag-Erling Smørgrav 358cf2b5f3bSDag-Erling Smørgrav return (result); 359cf2b5f3bSDag-Erling Smørgrav } 360cf2b5f3bSDag-Erling Smørgrav 361511b41d2SMark Murray /* 362511b41d2SMark Murray * Opens a TCP/IP connection to the remote server on the given host. 363511b41d2SMark Murray * The address of the remote host will be returned in hostaddr. 36480628bacSDag-Erling Smørgrav * If port is 0, the default port will be used. If needpriv is true, 365511b41d2SMark Murray * a privileged port will be allocated to make the connection. 36680628bacSDag-Erling Smørgrav * This requires super-user privileges if needpriv is true. 367511b41d2SMark Murray * Connection_attempts specifies the maximum number of tries (one per 368511b41d2SMark Murray * second). If proxy_command is non-NULL, it specifies the command (with %h 369511b41d2SMark Murray * and %p substituted for host and port, respectively) to use to contact 370511b41d2SMark Murray * the daemon. 371511b41d2SMark Murray */ 372511b41d2SMark Murray int 3731f5ce8f4SBrian Feldman ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 374d4af9e69SDag-Erling Smørgrav u_short port, int family, int connection_attempts, int *timeout_ms, 375d4af9e69SDag-Erling Smørgrav int want_keepalive, int needpriv, const char *proxy_command) 376511b41d2SMark Murray { 377511b41d2SMark Murray int gaierr; 378ca3176e7SBrian Feldman int on = 1; 379ca3176e7SBrian Feldman int sock = -1, attempt; 380ca3176e7SBrian Feldman char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 381ca3176e7SBrian Feldman struct addrinfo hints, *ai, *aitop; 382511b41d2SMark Murray 383e73e9afaSDag-Erling Smørgrav debug2("ssh_connect: needpriv %d", needpriv); 384511b41d2SMark Murray 385511b41d2SMark Murray /* If a proxy command is given, connect using it. */ 386511b41d2SMark Murray if (proxy_command != NULL) 38780628bacSDag-Erling Smørgrav return ssh_proxy_connect(host, port, proxy_command); 388511b41d2SMark Murray 389511b41d2SMark Murray /* No proxy command. */ 390511b41d2SMark Murray 391511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 392af12a3e7SDag-Erling Smørgrav hints.ai_family = family; 393511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 394a82e551fSDag-Erling Smørgrav snprintf(strport, sizeof strport, "%u", port); 3951f5ce8f4SBrian Feldman if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 396d4af9e69SDag-Erling Smørgrav fatal("%s: Could not resolve hostname %.100s: %s", __progname, 397d4af9e69SDag-Erling Smørgrav host, ssh_gai_strerror(gaierr)); 398511b41d2SMark Murray 399333ee039SDag-Erling Smørgrav for (attempt = 0; attempt < connection_attempts; attempt++) { 40062efe23aSDag-Erling Smørgrav if (attempt > 0) { 40162efe23aSDag-Erling Smørgrav /* Sleep a moment before retrying. */ 40262efe23aSDag-Erling Smørgrav sleep(1); 403511b41d2SMark Murray debug("Trying again..."); 40462efe23aSDag-Erling Smørgrav } 405333ee039SDag-Erling Smørgrav /* 406333ee039SDag-Erling Smørgrav * Loop through addresses for this host, and try each one in 407333ee039SDag-Erling Smørgrav * sequence until the connection succeeds. 408333ee039SDag-Erling Smørgrav */ 409511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 410511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 411511b41d2SMark Murray continue; 412511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 413511b41d2SMark Murray ntop, sizeof(ntop), strport, sizeof(strport), 414511b41d2SMark Murray NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 415511b41d2SMark Murray error("ssh_connect: getnameinfo failed"); 416511b41d2SMark Murray continue; 417511b41d2SMark Murray } 418511b41d2SMark Murray debug("Connecting to %.200s [%.100s] port %s.", 4191f5ce8f4SBrian Feldman host, ntop, strport); 420511b41d2SMark Murray 421511b41d2SMark Murray /* Create a socket for connecting. */ 422cf2b5f3bSDag-Erling Smørgrav sock = ssh_create_socket(needpriv, ai); 423511b41d2SMark Murray if (sock < 0) 424af12a3e7SDag-Erling Smørgrav /* Any error is already output */ 425511b41d2SMark Murray continue; 426511b41d2SMark Murray 427cf2b5f3bSDag-Erling Smørgrav if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 428d4af9e69SDag-Erling Smørgrav timeout_ms) >= 0) { 429511b41d2SMark Murray /* Successful connection. */ 430c322fe35SKris Kennaway memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 431511b41d2SMark Murray break; 432511b41d2SMark Murray } else { 433f388f5efSDag-Erling Smørgrav debug("connect to address %s port %s: %s", 434f388f5efSDag-Erling Smørgrav ntop, strport, strerror(errno)); 435511b41d2SMark Murray close(sock); 436333ee039SDag-Erling Smørgrav sock = -1; 437511b41d2SMark Murray } 438511b41d2SMark Murray } 439333ee039SDag-Erling Smørgrav if (sock != -1) 440511b41d2SMark Murray break; /* Successful connection. */ 441511b41d2SMark Murray } 442511b41d2SMark Murray 443511b41d2SMark Murray freeaddrinfo(aitop); 444511b41d2SMark Murray 445511b41d2SMark Murray /* Return failure if we didn't get a successful connection. */ 446333ee039SDag-Erling Smørgrav if (sock == -1) { 447aa49c926SDag-Erling Smørgrav error("ssh: connect to host %s port %s: %s", 448f388f5efSDag-Erling Smørgrav host, strport, strerror(errno)); 449aa49c926SDag-Erling Smørgrav return (-1); 450f388f5efSDag-Erling Smørgrav } 451511b41d2SMark Murray 452511b41d2SMark Murray debug("Connection established."); 453511b41d2SMark Murray 4541ec0d754SDag-Erling Smørgrav /* Set SO_KEEPALIVE if requested. */ 455d4af9e69SDag-Erling Smørgrav if (want_keepalive && 456ca3176e7SBrian Feldman setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 457ca3176e7SBrian Feldman sizeof(on)) < 0) 458ca3176e7SBrian Feldman error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 459ca3176e7SBrian Feldman 460511b41d2SMark Murray /* Set the connection. */ 461511b41d2SMark Murray packet_set_connection(sock, sock); 462d4af9e69SDag-Erling Smørgrav packet_set_timeout(options.server_alive_interval, 463d4af9e69SDag-Erling Smørgrav options.server_alive_count_max); 464511b41d2SMark Murray 465af12a3e7SDag-Erling Smørgrav return 0; 466511b41d2SMark Murray } 467511b41d2SMark Murray 4686888a9beSDag-Erling Smørgrav static void 4696888a9beSDag-Erling Smørgrav send_client_banner(int connection_out, int minor1) 4706888a9beSDag-Erling Smørgrav { 4716888a9beSDag-Erling Smørgrav /* Send our own protocol version identification. */ 4726888a9beSDag-Erling Smørgrav xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s%s%s", 4736888a9beSDag-Erling Smørgrav compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 4746888a9beSDag-Erling Smørgrav compat20 ? PROTOCOL_MINOR_2 : minor1, 4756888a9beSDag-Erling Smørgrav SSH_VERSION, options.hpn_disabled ? "" : SSH_VERSION_HPN, 4766888a9beSDag-Erling Smørgrav *options.version_addendum == '\0' ? "" : " ", 4776888a9beSDag-Erling Smørgrav options.version_addendum, compat20 ? "\r\n" : "\n"); 4786888a9beSDag-Erling Smørgrav if (roaming_atomicio(vwrite, connection_out, client_version_string, 4796888a9beSDag-Erling Smørgrav strlen(client_version_string)) != strlen(client_version_string)) 4806888a9beSDag-Erling Smørgrav fatal("write: %.100s", strerror(errno)); 4816888a9beSDag-Erling Smørgrav chop(client_version_string); 4826888a9beSDag-Erling Smørgrav debug("Local version string %.100s", client_version_string); 4836888a9beSDag-Erling Smørgrav } 4846888a9beSDag-Erling Smørgrav 485511b41d2SMark Murray /* 486e8aafc91SKris Kennaway * Waits for the server identification string, and sends our own 487e8aafc91SKris Kennaway * identification string. 488511b41d2SMark Murray */ 4897aee6ffeSDag-Erling Smørgrav void 490d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(int timeout_ms) 491511b41d2SMark Murray { 492e8aafc91SKris Kennaway char buf[256], remote_version[256]; /* must be same size! */ 493d4ecd108SDag-Erling Smørgrav int remote_major, remote_minor, mismatch; 494e8aafc91SKris Kennaway int connection_in = packet_get_connection_in(); 495e8aafc91SKris Kennaway int connection_out = packet_get_connection_out(); 4966888a9beSDag-Erling Smørgrav int minor1 = PROTOCOL_MINOR_1, client_banner_sent = 0; 497333ee039SDag-Erling Smørgrav u_int i, n; 498d4af9e69SDag-Erling Smørgrav size_t len; 499d4af9e69SDag-Erling Smørgrav int fdsetsz, remaining, rc; 500d4af9e69SDag-Erling Smørgrav struct timeval t_start, t_remaining; 501d4af9e69SDag-Erling Smørgrav fd_set *fdset; 502d4af9e69SDag-Erling Smørgrav 503d4af9e69SDag-Erling Smørgrav fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); 504d4af9e69SDag-Erling Smørgrav fdset = xcalloc(1, fdsetsz); 505511b41d2SMark Murray 5066888a9beSDag-Erling Smørgrav /* 5076888a9beSDag-Erling Smørgrav * If we are SSH2-only then we can send the banner immediately and 5086888a9beSDag-Erling Smørgrav * save a round-trip. 5096888a9beSDag-Erling Smørgrav */ 5106888a9beSDag-Erling Smørgrav if (options.protocol == SSH_PROTO_2) { 5116888a9beSDag-Erling Smørgrav enable_compat20(); 5126888a9beSDag-Erling Smørgrav send_client_banner(connection_out, 0); 5136888a9beSDag-Erling Smørgrav client_banner_sent = 1; 5146888a9beSDag-Erling Smørgrav } 5156888a9beSDag-Erling Smørgrav 516d4ecd108SDag-Erling Smørgrav /* Read other side's version identification. */ 517d4af9e69SDag-Erling Smørgrav remaining = timeout_ms; 518333ee039SDag-Erling Smørgrav for (n = 0;;) { 519e8aafc91SKris Kennaway for (i = 0; i < sizeof(buf) - 1; i++) { 520d4af9e69SDag-Erling Smørgrav if (timeout_ms > 0) { 521d4af9e69SDag-Erling Smørgrav gettimeofday(&t_start, NULL); 522d4af9e69SDag-Erling Smørgrav ms_to_timeval(&t_remaining, remaining); 523d4af9e69SDag-Erling Smørgrav FD_SET(connection_in, fdset); 524d4af9e69SDag-Erling Smørgrav rc = select(connection_in + 1, fdset, NULL, 525d4af9e69SDag-Erling Smørgrav fdset, &t_remaining); 526d4af9e69SDag-Erling Smørgrav ms_subtract_diff(&t_start, &remaining); 527d4af9e69SDag-Erling Smørgrav if (rc == 0 || remaining <= 0) 528d4af9e69SDag-Erling Smørgrav fatal("Connection timed out during " 529d4af9e69SDag-Erling Smørgrav "banner exchange"); 530d4af9e69SDag-Erling Smørgrav if (rc == -1) { 531d4af9e69SDag-Erling Smørgrav if (errno == EINTR) 532d4af9e69SDag-Erling Smørgrav continue; 533d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 534d4af9e69SDag-Erling Smørgrav "select: %s", strerror(errno)); 535d4af9e69SDag-Erling Smørgrav } 536d4af9e69SDag-Erling Smørgrav } 537d4af9e69SDag-Erling Smørgrav 5387aee6ffeSDag-Erling Smørgrav len = roaming_atomicio(read, connection_in, &buf[i], 1); 539d4ecd108SDag-Erling Smørgrav 540d4ecd108SDag-Erling Smørgrav if (len != 1 && errno == EPIPE) 541d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 542d4af9e69SDag-Erling Smørgrav "Connection closed by remote host"); 543d4ecd108SDag-Erling Smørgrav else if (len != 1) 544d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 545d4af9e69SDag-Erling Smørgrav "read: %.100s", strerror(errno)); 546e8aafc91SKris Kennaway if (buf[i] == '\r') { 547e8aafc91SKris Kennaway buf[i] = '\n'; 548e8aafc91SKris Kennaway buf[i + 1] = 0; 549e8aafc91SKris Kennaway continue; /**XXX wait for \n */ 550511b41d2SMark Murray } 551e8aafc91SKris Kennaway if (buf[i] == '\n') { 552e8aafc91SKris Kennaway buf[i + 1] = 0; 553511b41d2SMark Murray break; 554e8aafc91SKris Kennaway } 555333ee039SDag-Erling Smørgrav if (++n > 65536) 556d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 557d4af9e69SDag-Erling Smørgrav "No banner received"); 558e8aafc91SKris Kennaway } 559e8aafc91SKris Kennaway buf[sizeof(buf) - 1] = 0; 560c2d3a559SKris Kennaway if (strncmp(buf, "SSH-", 4) == 0) 561c2d3a559SKris Kennaway break; 562c2d3a559SKris Kennaway debug("ssh_exchange_identification: %s", buf); 563c2d3a559SKris Kennaway } 564e8aafc91SKris Kennaway server_version_string = xstrdup(buf); 565d4af9e69SDag-Erling Smørgrav xfree(fdset); 566511b41d2SMark Murray 567511b41d2SMark Murray /* 568e8aafc91SKris Kennaway * Check that the versions match. In future this might accept 569e8aafc91SKris Kennaway * several versions and set appropriate flags to handle them. 570511b41d2SMark Murray */ 571e8aafc91SKris Kennaway if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 572e8aafc91SKris Kennaway &remote_major, &remote_minor, remote_version) != 3) 573e8aafc91SKris Kennaway fatal("Bad remote protocol version identification: '%.100s'", buf); 574e8aafc91SKris Kennaway debug("Remote protocol version %d.%d, remote software version %.100s", 575e8aafc91SKris Kennaway remote_major, remote_minor, remote_version); 576511b41d2SMark Murray 577e8aafc91SKris Kennaway compat_datafellows(remote_version); 578e8aafc91SKris Kennaway mismatch = 0; 579e8aafc91SKris Kennaway 580e8aafc91SKris Kennaway switch (remote_major) { 581e8aafc91SKris Kennaway case 1: 582e8aafc91SKris Kennaway if (remote_minor == 99 && 583e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) && 584e8aafc91SKris Kennaway !(options.protocol & SSH_PROTO_1_PREFERRED)) { 585e8aafc91SKris Kennaway enable_compat20(); 586511b41d2SMark Murray break; 587e8aafc91SKris Kennaway } 588e8aafc91SKris Kennaway if (!(options.protocol & SSH_PROTO_1)) { 589e8aafc91SKris Kennaway mismatch = 1; 590e8aafc91SKris Kennaway break; 591e8aafc91SKris Kennaway } 592e8aafc91SKris Kennaway if (remote_minor < 3) { 593e8aafc91SKris Kennaway fatal("Remote machine has too old SSH software version."); 594ca3176e7SBrian Feldman } else if (remote_minor == 3 || remote_minor == 4) { 595e8aafc91SKris Kennaway /* We speak 1.3, too. */ 596e8aafc91SKris Kennaway enable_compat13(); 597ca3176e7SBrian Feldman minor1 = 3; 598e8aafc91SKris Kennaway if (options.forward_agent) { 599cf2b5f3bSDag-Erling Smørgrav logit("Agent forwarding disabled for protocol 1.3"); 600e8aafc91SKris Kennaway options.forward_agent = 0; 601e8aafc91SKris Kennaway } 602e8aafc91SKris Kennaway } 603e8aafc91SKris Kennaway break; 604e8aafc91SKris Kennaway case 2: 605e8aafc91SKris Kennaway if (options.protocol & SSH_PROTO_2) { 606e8aafc91SKris Kennaway enable_compat20(); 607e8aafc91SKris Kennaway break; 608e8aafc91SKris Kennaway } 609e8aafc91SKris Kennaway /* FALLTHROUGH */ 610511b41d2SMark Murray default: 611e8aafc91SKris Kennaway mismatch = 1; 612e8aafc91SKris Kennaway break; 613511b41d2SMark Murray } 614e8aafc91SKris Kennaway if (mismatch) 615e8aafc91SKris Kennaway fatal("Protocol major versions differ: %d vs. %d", 616e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 617e8aafc91SKris Kennaway remote_major); 6186888a9beSDag-Erling Smørgrav if (!client_banner_sent) 6196888a9beSDag-Erling Smørgrav send_client_banner(connection_out, minor1); 620e8aafc91SKris Kennaway chop(server_version_string); 621511b41d2SMark Murray } 622511b41d2SMark Murray 623ca3176e7SBrian Feldman /* defaults to 'no' */ 624af12a3e7SDag-Erling Smørgrav static int 625af12a3e7SDag-Erling Smørgrav confirm(const char *prompt) 626511b41d2SMark Murray { 627af12a3e7SDag-Erling Smørgrav const char *msg, *again = "Please type 'yes' or 'no': "; 628af12a3e7SDag-Erling Smørgrav char *p; 629af12a3e7SDag-Erling Smørgrav int ret = -1; 630511b41d2SMark Murray 631ca3176e7SBrian Feldman if (options.batch_mode) 632ca3176e7SBrian Feldman return 0; 633af12a3e7SDag-Erling Smørgrav for (msg = prompt;;msg = again) { 634af12a3e7SDag-Erling Smørgrav p = read_passphrase(msg, RP_ECHO); 635af12a3e7SDag-Erling Smørgrav if (p == NULL || 636af12a3e7SDag-Erling Smørgrav (p[0] == '\0') || (p[0] == '\n') || 637af12a3e7SDag-Erling Smørgrav strncasecmp(p, "no", 2) == 0) 638af12a3e7SDag-Erling Smørgrav ret = 0; 639f388f5efSDag-Erling Smørgrav if (p && strncasecmp(p, "yes", 3) == 0) 640af12a3e7SDag-Erling Smørgrav ret = 1; 641af12a3e7SDag-Erling Smørgrav if (p) 642af12a3e7SDag-Erling Smørgrav xfree(p); 643af12a3e7SDag-Erling Smørgrav if (ret != -1) 644af12a3e7SDag-Erling Smørgrav return ret; 645511b41d2SMark Murray } 646511b41d2SMark Murray } 647511b41d2SMark Murray 648b15c8340SDag-Erling Smørgrav static int 649b15c8340SDag-Erling Smørgrav check_host_cert(const char *host, const Key *host_key) 650b15c8340SDag-Erling Smørgrav { 651b15c8340SDag-Erling Smørgrav const char *reason; 652b15c8340SDag-Erling Smørgrav 653b15c8340SDag-Erling Smørgrav if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { 654b15c8340SDag-Erling Smørgrav error("%s", reason); 655b15c8340SDag-Erling Smørgrav return 0; 656b15c8340SDag-Erling Smørgrav } 657e2f6069cSDag-Erling Smørgrav if (buffer_len(&host_key->cert->critical) != 0) { 658e2f6069cSDag-Erling Smørgrav error("Certificate for %s contains unsupported " 659e2f6069cSDag-Erling Smørgrav "critical options(s)", host); 660b15c8340SDag-Erling Smørgrav return 0; 661b15c8340SDag-Erling Smørgrav } 662b15c8340SDag-Erling Smørgrav return 1; 663b15c8340SDag-Erling Smørgrav } 664b15c8340SDag-Erling Smørgrav 6654a421b63SDag-Erling Smørgrav static int 6664a421b63SDag-Erling Smørgrav sockaddr_is_local(struct sockaddr *hostaddr) 6674a421b63SDag-Erling Smørgrav { 6684a421b63SDag-Erling Smørgrav switch (hostaddr->sa_family) { 6694a421b63SDag-Erling Smørgrav case AF_INET: 6704a421b63SDag-Erling Smørgrav return (ntohl(((struct sockaddr_in *)hostaddr)-> 6714a421b63SDag-Erling Smørgrav sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 6724a421b63SDag-Erling Smørgrav case AF_INET6: 6734a421b63SDag-Erling Smørgrav return IN6_IS_ADDR_LOOPBACK( 6744a421b63SDag-Erling Smørgrav &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 6754a421b63SDag-Erling Smørgrav default: 6764a421b63SDag-Erling Smørgrav return 0; 6774a421b63SDag-Erling Smørgrav } 6784a421b63SDag-Erling Smørgrav } 6794a421b63SDag-Erling Smørgrav 6804a421b63SDag-Erling Smørgrav /* 6814a421b63SDag-Erling Smørgrav * Prepare the hostname and ip address strings that are used to lookup 6824a421b63SDag-Erling Smørgrav * host keys in known_hosts files. These may have a port number appended. 6834a421b63SDag-Erling Smørgrav */ 6844a421b63SDag-Erling Smørgrav void 6854a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, 6864a421b63SDag-Erling Smørgrav u_short port, char **hostfile_hostname, char **hostfile_ipaddr) 6874a421b63SDag-Erling Smørgrav { 6884a421b63SDag-Erling Smørgrav char ntop[NI_MAXHOST]; 6894a421b63SDag-Erling Smørgrav socklen_t addrlen; 6904a421b63SDag-Erling Smørgrav 6914a421b63SDag-Erling Smørgrav switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { 6924a421b63SDag-Erling Smørgrav case -1: 6934a421b63SDag-Erling Smørgrav addrlen = 0; 6944a421b63SDag-Erling Smørgrav break; 6954a421b63SDag-Erling Smørgrav case AF_INET: 6964a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr_in); 6974a421b63SDag-Erling Smørgrav break; 6984a421b63SDag-Erling Smørgrav case AF_INET6: 6994a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr_in6); 7004a421b63SDag-Erling Smørgrav break; 7014a421b63SDag-Erling Smørgrav default: 7024a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr); 7034a421b63SDag-Erling Smørgrav break; 7044a421b63SDag-Erling Smørgrav } 7054a421b63SDag-Erling Smørgrav 7064a421b63SDag-Erling Smørgrav /* 7074a421b63SDag-Erling Smørgrav * We don't have the remote ip-address for connections 7084a421b63SDag-Erling Smørgrav * using a proxy command 7094a421b63SDag-Erling Smørgrav */ 7104a421b63SDag-Erling Smørgrav if (hostfile_ipaddr != NULL) { 7114a421b63SDag-Erling Smørgrav if (options.proxy_command == NULL) { 7124a421b63SDag-Erling Smørgrav if (getnameinfo(hostaddr, addrlen, 7134a421b63SDag-Erling Smørgrav ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) 7144a421b63SDag-Erling Smørgrav fatal("check_host_key: getnameinfo failed"); 7154a421b63SDag-Erling Smørgrav *hostfile_ipaddr = put_host_port(ntop, port); 7164a421b63SDag-Erling Smørgrav } else { 7174a421b63SDag-Erling Smørgrav *hostfile_ipaddr = xstrdup("<no hostip for proxy " 7184a421b63SDag-Erling Smørgrav "command>"); 7194a421b63SDag-Erling Smørgrav } 7204a421b63SDag-Erling Smørgrav } 7214a421b63SDag-Erling Smørgrav 7224a421b63SDag-Erling Smørgrav /* 7234a421b63SDag-Erling Smørgrav * Allow the user to record the key under a different name or 7244a421b63SDag-Erling Smørgrav * differentiate a non-standard port. This is useful for ssh 7254a421b63SDag-Erling Smørgrav * tunneling over forwarded connections or if you run multiple 7264a421b63SDag-Erling Smørgrav * sshd's on different ports on the same machine. 7274a421b63SDag-Erling Smørgrav */ 7284a421b63SDag-Erling Smørgrav if (hostfile_hostname != NULL) { 7294a421b63SDag-Erling Smørgrav if (options.host_key_alias != NULL) { 7304a421b63SDag-Erling Smørgrav *hostfile_hostname = xstrdup(options.host_key_alias); 7314a421b63SDag-Erling Smørgrav debug("using hostkeyalias: %s", *hostfile_hostname); 7324a421b63SDag-Erling Smørgrav } else { 7334a421b63SDag-Erling Smørgrav *hostfile_hostname = put_host_port(hostname, port); 7344a421b63SDag-Erling Smørgrav } 7354a421b63SDag-Erling Smørgrav } 7364a421b63SDag-Erling Smørgrav } 7374a421b63SDag-Erling Smørgrav 738e8aafc91SKris Kennaway /* 739af12a3e7SDag-Erling Smørgrav * check whether the supplied host key is valid, return -1 if the key 740e146993eSDag-Erling Smørgrav * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. 741e8aafc91SKris Kennaway */ 742333ee039SDag-Erling Smørgrav #define RDRW 0 743333ee039SDag-Erling Smørgrav #define RDONLY 1 744333ee039SDag-Erling Smørgrav #define ROQUIET 2 745af12a3e7SDag-Erling Smørgrav static int 746333ee039SDag-Erling Smørgrav check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 747e146993eSDag-Erling Smørgrav Key *host_key, int readonly, 748e146993eSDag-Erling Smørgrav char **user_hostfiles, u_int num_user_hostfiles, 749e146993eSDag-Erling Smørgrav char **system_hostfiles, u_int num_system_hostfiles) 750511b41d2SMark Murray { 751e8aafc91SKris Kennaway HostStatus host_status; 752e8aafc91SKris Kennaway HostStatus ip_status; 753e146993eSDag-Erling Smørgrav Key *raw_key = NULL; 754e146993eSDag-Erling Smørgrav char *ip = NULL, *host = NULL; 755e146993eSDag-Erling Smørgrav char hostline[1000], *hostp, *fp, *ra; 756af12a3e7SDag-Erling Smørgrav char msg[1024]; 757e146993eSDag-Erling Smørgrav const char *type; 7584a421b63SDag-Erling Smørgrav const struct hostkey_entry *host_found, *ip_found; 759e146993eSDag-Erling Smørgrav int len, cancelled_forwarding = 0; 760e146993eSDag-Erling Smørgrav int local = sockaddr_is_local(hostaddr); 761e146993eSDag-Erling Smørgrav int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; 762e146993eSDag-Erling Smørgrav struct hostkeys *host_hostkeys, *ip_hostkeys; 763e146993eSDag-Erling Smørgrav u_int i; 764511b41d2SMark Murray 765e8aafc91SKris Kennaway /* 766e8aafc91SKris Kennaway * Force accepting of the host key for loopback/localhost. The 767e8aafc91SKris Kennaway * problem is that if the home directory is NFS-mounted to multiple 768e8aafc91SKris Kennaway * machines, localhost will refer to a different machine in each of 769e8aafc91SKris Kennaway * them, and the user will get bogus HOST_CHANGED warnings. This 770e8aafc91SKris Kennaway * essentially disables host authentication for localhost; however, 771e8aafc91SKris Kennaway * this is probably not a real problem. 772e8aafc91SKris Kennaway */ 773af12a3e7SDag-Erling Smørgrav if (options.no_host_authentication_for_localhost == 1 && local && 774af12a3e7SDag-Erling Smørgrav options.host_key_alias == NULL) { 775ca3176e7SBrian Feldman debug("Forcing accepting of host key for " 776ca3176e7SBrian Feldman "loopback/localhost."); 777af12a3e7SDag-Erling Smørgrav return 0; 778511b41d2SMark Murray } 779511b41d2SMark Murray 780e8aafc91SKris Kennaway /* 7814a421b63SDag-Erling Smørgrav * Prepare the hostname and address strings used for hostkey lookup. 7824a421b63SDag-Erling Smørgrav * In some cases, these will have a port number appended. 783e8aafc91SKris Kennaway */ 7844a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); 785d4af9e69SDag-Erling Smørgrav 786ca3176e7SBrian Feldman /* 787ca3176e7SBrian Feldman * Turn off check_host_ip if the connection is to localhost, via proxy 788ca3176e7SBrian Feldman * command or if we don't have a hostname to compare with 789ca3176e7SBrian Feldman */ 790333ee039SDag-Erling Smørgrav if (options.check_host_ip && (local || 791333ee039SDag-Erling Smørgrav strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 792ca3176e7SBrian Feldman options.check_host_ip = 0; 793ca3176e7SBrian Feldman 7944a421b63SDag-Erling Smørgrav host_hostkeys = init_hostkeys(); 795e146993eSDag-Erling Smørgrav for (i = 0; i < num_user_hostfiles; i++) 796e146993eSDag-Erling Smørgrav load_hostkeys(host_hostkeys, host, user_hostfiles[i]); 797e146993eSDag-Erling Smørgrav for (i = 0; i < num_system_hostfiles; i++) 798e146993eSDag-Erling Smørgrav load_hostkeys(host_hostkeys, host, system_hostfiles[i]); 7994a421b63SDag-Erling Smørgrav 8004a421b63SDag-Erling Smørgrav ip_hostkeys = NULL; 8014a421b63SDag-Erling Smørgrav if (!want_cert && options.check_host_ip) { 8024a421b63SDag-Erling Smørgrav ip_hostkeys = init_hostkeys(); 803e146993eSDag-Erling Smørgrav for (i = 0; i < num_user_hostfiles; i++) 804e146993eSDag-Erling Smørgrav load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]); 805e146993eSDag-Erling Smørgrav for (i = 0; i < num_system_hostfiles; i++) 806e146993eSDag-Erling Smørgrav load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]); 807e8aafc91SKris Kennaway } 808e8aafc91SKris Kennaway 809b15c8340SDag-Erling Smørgrav retry: 8104a421b63SDag-Erling Smørgrav /* Reload these as they may have changed on cert->key downgrade */ 811b15c8340SDag-Erling Smørgrav want_cert = key_is_cert(host_key); 812b15c8340SDag-Erling Smørgrav type = key_type(host_key); 813b15c8340SDag-Erling Smørgrav 814e8aafc91SKris Kennaway /* 815b74df5b2SDag-Erling Smørgrav * Check if the host key is present in the user's list of known 816e8aafc91SKris Kennaway * hosts or in the systemwide list. 817e8aafc91SKris Kennaway */ 8184a421b63SDag-Erling Smørgrav host_status = check_key_in_hostkeys(host_hostkeys, host_key, 8194a421b63SDag-Erling Smørgrav &host_found); 8204a421b63SDag-Erling Smørgrav 821e8aafc91SKris Kennaway /* 822e8aafc91SKris Kennaway * Also perform check for the ip address, skip the check if we are 823b15c8340SDag-Erling Smørgrav * localhost, looking for a certificate, or the hostname was an ip 824b15c8340SDag-Erling Smørgrav * address to begin with. 825e8aafc91SKris Kennaway */ 8264a421b63SDag-Erling Smørgrav if (!want_cert && ip_hostkeys != NULL) { 8274a421b63SDag-Erling Smørgrav ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, 8284a421b63SDag-Erling Smørgrav &ip_found); 829e8aafc91SKris Kennaway if (host_status == HOST_CHANGED && 8304a421b63SDag-Erling Smørgrav (ip_status != HOST_CHANGED || 8314a421b63SDag-Erling Smørgrav (ip_found != NULL && 8324a421b63SDag-Erling Smørgrav !key_equal(ip_found->key, host_found->key)))) 833e8aafc91SKris Kennaway host_ip_differ = 1; 834e8aafc91SKris Kennaway } else 835e8aafc91SKris Kennaway ip_status = host_status; 836e8aafc91SKris Kennaway 837e8aafc91SKris Kennaway switch (host_status) { 838e8aafc91SKris Kennaway case HOST_OK: 839e8aafc91SKris Kennaway /* The host is known and the key matches. */ 840b15c8340SDag-Erling Smørgrav debug("Host '%.200s' is known and matches the %s host %s.", 841b15c8340SDag-Erling Smørgrav host, type, want_cert ? "certificate" : "key"); 8424a421b63SDag-Erling Smørgrav debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", 8434a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 844b15c8340SDag-Erling Smørgrav if (want_cert && !check_host_cert(hostname, host_key)) 845b15c8340SDag-Erling Smørgrav goto fail; 846ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 847b15c8340SDag-Erling Smørgrav if (readonly || want_cert) 848cf2b5f3bSDag-Erling Smørgrav logit("%s host key for IP address " 849af12a3e7SDag-Erling Smørgrav "'%.128s' not in list of known hosts.", 850e8aafc91SKris Kennaway type, ip); 851e146993eSDag-Erling Smørgrav else if (!add_host_to_hostfile(user_hostfiles[0], ip, 852aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts)) 853cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the %s host key for IP " 854af12a3e7SDag-Erling Smørgrav "address '%.128s' to the list of known " 855e146993eSDag-Erling Smørgrav "hosts (%.30s).", type, ip, 856e146993eSDag-Erling Smørgrav user_hostfiles[0]); 857af12a3e7SDag-Erling Smørgrav else 858cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added the %s host " 859af12a3e7SDag-Erling Smørgrav "key for IP address '%.128s' to the list " 860af12a3e7SDag-Erling Smørgrav "of known hosts.", type, ip); 861d4af9e69SDag-Erling Smørgrav } else if (options.visual_host_key) { 862d4af9e69SDag-Erling Smørgrav fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 863d4af9e69SDag-Erling Smørgrav ra = key_fingerprint(host_key, SSH_FP_MD5, 864d4af9e69SDag-Erling Smørgrav SSH_FP_RANDOMART); 865d4af9e69SDag-Erling Smørgrav logit("Host key fingerprint is %s\n%s\n", fp, ra); 866d4af9e69SDag-Erling Smørgrav xfree(ra); 867d4af9e69SDag-Erling Smørgrav xfree(fp); 868e8aafc91SKris Kennaway } 869e8aafc91SKris Kennaway break; 870e8aafc91SKris Kennaway case HOST_NEW: 871333ee039SDag-Erling Smørgrav if (options.host_key_alias == NULL && port != 0 && 872333ee039SDag-Erling Smørgrav port != SSH_DEFAULT_PORT) { 873333ee039SDag-Erling Smørgrav debug("checking without port identifier"); 874cce7d346SDag-Erling Smørgrav if (check_host_key(hostname, hostaddr, 0, host_key, 875e146993eSDag-Erling Smørgrav ROQUIET, user_hostfiles, num_user_hostfiles, 876e146993eSDag-Erling Smørgrav system_hostfiles, num_system_hostfiles) == 0) { 877333ee039SDag-Erling Smørgrav debug("found matching key w/out port"); 878333ee039SDag-Erling Smørgrav break; 879333ee039SDag-Erling Smørgrav } 880333ee039SDag-Erling Smørgrav } 881b15c8340SDag-Erling Smørgrav if (readonly || want_cert) 882af12a3e7SDag-Erling Smørgrav goto fail; 883e8aafc91SKris Kennaway /* The host is new. */ 884e8aafc91SKris Kennaway if (options.strict_host_key_checking == 1) { 885af12a3e7SDag-Erling Smørgrav /* 886af12a3e7SDag-Erling Smørgrav * User has requested strict host key checking. We 887af12a3e7SDag-Erling Smørgrav * will not add the host key automatically. The only 888af12a3e7SDag-Erling Smørgrav * alternative left is to abort. 889af12a3e7SDag-Erling Smørgrav */ 890af12a3e7SDag-Erling Smørgrav error("No %s host key is known for %.200s and you " 891af12a3e7SDag-Erling Smørgrav "have requested strict checking.", type, host); 892af12a3e7SDag-Erling Smørgrav goto fail; 893e8aafc91SKris Kennaway } else if (options.strict_host_key_checking == 2) { 894cf2b5f3bSDag-Erling Smørgrav char msg1[1024], msg2[1024]; 895cf2b5f3bSDag-Erling Smørgrav 8964a421b63SDag-Erling Smørgrav if (show_other_keys(host_hostkeys, host_key)) 897cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), 898cf2b5f3bSDag-Erling Smørgrav "\nbut keys of different type are already" 899cf2b5f3bSDag-Erling Smørgrav " known for this host."); 900cf2b5f3bSDag-Erling Smørgrav else 901cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), "."); 902e8aafc91SKris Kennaway /* The default */ 903ca3176e7SBrian Feldman fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 904d4af9e69SDag-Erling Smørgrav ra = key_fingerprint(host_key, SSH_FP_MD5, 905d4af9e69SDag-Erling Smørgrav SSH_FP_RANDOMART); 906cf2b5f3bSDag-Erling Smørgrav msg2[0] = '\0'; 907cf2b5f3bSDag-Erling Smørgrav if (options.verify_host_key_dns) { 9081ec0d754SDag-Erling Smørgrav if (matching_host_key_dns) 909cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 910cf2b5f3bSDag-Erling Smørgrav "Matching host key fingerprint" 911cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 912cf2b5f3bSDag-Erling Smørgrav else 913cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 914cf2b5f3bSDag-Erling Smørgrav "No matching host key fingerprint" 915cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 916cf2b5f3bSDag-Erling Smørgrav } 917af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 918af12a3e7SDag-Erling Smørgrav "The authenticity of host '%.200s (%s)' can't be " 919f388f5efSDag-Erling Smørgrav "established%s\n" 920d4af9e69SDag-Erling Smørgrav "%s key fingerprint is %s.%s%s\n%s" 921af12a3e7SDag-Erling Smørgrav "Are you sure you want to continue connecting " 922f388f5efSDag-Erling Smørgrav "(yes/no)? ", 923d4af9e69SDag-Erling Smørgrav host, ip, msg1, type, fp, 924d4af9e69SDag-Erling Smørgrav options.visual_host_key ? "\n" : "", 925d4af9e69SDag-Erling Smørgrav options.visual_host_key ? ra : "", 926d4af9e69SDag-Erling Smørgrav msg2); 927d4af9e69SDag-Erling Smørgrav xfree(ra); 928ca3176e7SBrian Feldman xfree(fp); 929af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 930af12a3e7SDag-Erling Smørgrav goto fail; 931e8aafc91SKris Kennaway } 932af12a3e7SDag-Erling Smørgrav /* 933af12a3e7SDag-Erling Smørgrav * If not in strict mode, add the key automatically to the 934af12a3e7SDag-Erling Smørgrav * local known_hosts file. 935af12a3e7SDag-Erling Smørgrav */ 936aa49c926SDag-Erling Smørgrav if (options.check_host_ip && ip_status == HOST_NEW) { 9374a421b63SDag-Erling Smørgrav snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 938aa49c926SDag-Erling Smørgrav hostp = hostline; 939aa49c926SDag-Erling Smørgrav if (options.hash_known_hosts) { 940aa49c926SDag-Erling Smørgrav /* Add hash of host and IP separately */ 941e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], 942e146993eSDag-Erling Smørgrav host, host_key, options.hash_known_hosts) && 943e146993eSDag-Erling Smørgrav add_host_to_hostfile(user_hostfiles[0], ip, 944aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts); 945aa49c926SDag-Erling Smørgrav } else { 946aa49c926SDag-Erling Smørgrav /* Add unhashed "host,ip" */ 947e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], 948aa49c926SDag-Erling Smørgrav hostline, host_key, 949aa49c926SDag-Erling Smørgrav options.hash_known_hosts); 950aa49c926SDag-Erling Smørgrav } 951aa49c926SDag-Erling Smørgrav } else { 952e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], host, 953e146993eSDag-Erling Smørgrav host_key, options.hash_known_hosts); 954aa49c926SDag-Erling Smørgrav hostp = host; 955aa49c926SDag-Erling Smørgrav } 956aa49c926SDag-Erling Smørgrav 957aa49c926SDag-Erling Smørgrav if (!r) 958cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the host to the list of known " 959e146993eSDag-Erling Smørgrav "hosts (%.500s).", user_hostfiles[0]); 960e8aafc91SKris Kennaway else 961cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added '%.200s' (%s) to the " 962af12a3e7SDag-Erling Smørgrav "list of known hosts.", hostp, type); 963e8aafc91SKris Kennaway break; 964b15c8340SDag-Erling Smørgrav case HOST_REVOKED: 965b15c8340SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 966b15c8340SDag-Erling Smørgrav error("@ WARNING: REVOKED HOST KEY DETECTED! @"); 967b15c8340SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 968b15c8340SDag-Erling Smørgrav error("The %s host key for %s is marked as revoked.", type, host); 969b15c8340SDag-Erling Smørgrav error("This could mean that a stolen key is being used to"); 970b15c8340SDag-Erling Smørgrav error("impersonate this host."); 971b15c8340SDag-Erling Smørgrav 972b15c8340SDag-Erling Smørgrav /* 973b15c8340SDag-Erling Smørgrav * If strict host key checking is in use, the user will have 974b15c8340SDag-Erling Smørgrav * to edit the key manually and we can only abort. 975b15c8340SDag-Erling Smørgrav */ 976b15c8340SDag-Erling Smørgrav if (options.strict_host_key_checking) { 977b15c8340SDag-Erling Smørgrav error("%s host key for %.200s was revoked and you have " 978b15c8340SDag-Erling Smørgrav "requested strict checking.", type, host); 979b15c8340SDag-Erling Smørgrav goto fail; 980b15c8340SDag-Erling Smørgrav } 981b15c8340SDag-Erling Smørgrav goto continue_unsafe; 982b15c8340SDag-Erling Smørgrav 983e8aafc91SKris Kennaway case HOST_CHANGED: 984b15c8340SDag-Erling Smørgrav if (want_cert) { 985b15c8340SDag-Erling Smørgrav /* 986b15c8340SDag-Erling Smørgrav * This is only a debug() since it is valid to have 987b15c8340SDag-Erling Smørgrav * CAs with wildcard DNS matches that don't match 988b15c8340SDag-Erling Smørgrav * all hosts that one might visit. 989b15c8340SDag-Erling Smørgrav */ 990b15c8340SDag-Erling Smørgrav debug("Host certificate authority does not " 9914a421b63SDag-Erling Smørgrav "match %s in %s:%lu", CA_MARKER, 9924a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 993b15c8340SDag-Erling Smørgrav goto fail; 994b15c8340SDag-Erling Smørgrav } 995333ee039SDag-Erling Smørgrav if (readonly == ROQUIET) 996333ee039SDag-Erling Smørgrav goto fail; 997e8aafc91SKris Kennaway if (options.check_host_ip && host_ip_differ) { 99821e764dfSDag-Erling Smørgrav char *key_msg; 999e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 100021e764dfSDag-Erling Smørgrav key_msg = "is unknown"; 1001e8aafc91SKris Kennaway else if (ip_status == HOST_OK) 100221e764dfSDag-Erling Smørgrav key_msg = "is unchanged"; 1003e8aafc91SKris Kennaway else 100421e764dfSDag-Erling Smørgrav key_msg = "has a different value"; 1005e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1006e8aafc91SKris Kennaway error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 1007e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1008e8aafc91SKris Kennaway error("The %s host key for %s has changed,", type, host); 1009d4af9e69SDag-Erling Smørgrav error("and the key for the corresponding IP address %s", ip); 101021e764dfSDag-Erling Smørgrav error("%s. This could either mean that", key_msg); 1011e8aafc91SKris Kennaway error("DNS SPOOFING is happening or the IP address for the host"); 1012ca3176e7SBrian Feldman error("and its host key have changed at the same time."); 1013ca3176e7SBrian Feldman if (ip_status != HOST_NEW) 10144a421b63SDag-Erling Smørgrav error("Offending key for IP in %s:%lu", 10154a421b63SDag-Erling Smørgrav ip_found->file, ip_found->line); 1016e8aafc91SKris Kennaway } 1017e8aafc91SKris Kennaway /* The host key has changed. */ 10181ec0d754SDag-Erling Smørgrav warn_changed_key(host_key); 1019e8aafc91SKris Kennaway error("Add correct host key in %.100s to get rid of this message.", 1020e146993eSDag-Erling Smørgrav user_hostfiles[0]); 10214a421b63SDag-Erling Smørgrav error("Offending %s key in %s:%lu", key_type(host_found->key), 10224a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1023e8aafc91SKris Kennaway 1024e8aafc91SKris Kennaway /* 1025e8aafc91SKris Kennaway * If strict host key checking is in use, the user will have 1026e8aafc91SKris Kennaway * to edit the key manually and we can only abort. 1027e8aafc91SKris Kennaway */ 1028af12a3e7SDag-Erling Smørgrav if (options.strict_host_key_checking) { 1029af12a3e7SDag-Erling Smørgrav error("%s host key for %.200s has changed and you have " 1030af12a3e7SDag-Erling Smørgrav "requested strict checking.", type, host); 1031af12a3e7SDag-Erling Smørgrav goto fail; 1032af12a3e7SDag-Erling Smørgrav } 1033e8aafc91SKris Kennaway 1034b15c8340SDag-Erling Smørgrav continue_unsafe: 1035e8aafc91SKris Kennaway /* 1036e8aafc91SKris Kennaway * If strict host key checking has not been requested, allow 1037cf2b5f3bSDag-Erling Smørgrav * the connection but without MITM-able authentication or 1038333ee039SDag-Erling Smørgrav * forwarding. 1039e8aafc91SKris Kennaway */ 1040e8aafc91SKris Kennaway if (options.password_authentication) { 1041af12a3e7SDag-Erling Smørgrav error("Password authentication is disabled to avoid " 1042af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1043e8aafc91SKris Kennaway options.password_authentication = 0; 1044d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1045e8aafc91SKris Kennaway } 1046cf2b5f3bSDag-Erling Smørgrav if (options.kbd_interactive_authentication) { 1047cf2b5f3bSDag-Erling Smørgrav error("Keyboard-interactive authentication is disabled" 1048cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 1049cf2b5f3bSDag-Erling Smørgrav options.kbd_interactive_authentication = 0; 1050cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 1051d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1052cf2b5f3bSDag-Erling Smørgrav } 1053cf2b5f3bSDag-Erling Smørgrav if (options.challenge_response_authentication) { 1054cf2b5f3bSDag-Erling Smørgrav error("Challenge/response authentication is disabled" 1055cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 1056cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 1057d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1058cf2b5f3bSDag-Erling Smørgrav } 1059e8aafc91SKris Kennaway if (options.forward_agent) { 1060af12a3e7SDag-Erling Smørgrav error("Agent forwarding is disabled to avoid " 1061af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1062e8aafc91SKris Kennaway options.forward_agent = 0; 1063d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1064e8aafc91SKris Kennaway } 1065ca3176e7SBrian Feldman if (options.forward_x11) { 1066af12a3e7SDag-Erling Smørgrav error("X11 forwarding is disabled to avoid " 1067af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1068ca3176e7SBrian Feldman options.forward_x11 = 0; 1069d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1070ca3176e7SBrian Feldman } 1071af12a3e7SDag-Erling Smørgrav if (options.num_local_forwards > 0 || 1072af12a3e7SDag-Erling Smørgrav options.num_remote_forwards > 0) { 1073af12a3e7SDag-Erling Smørgrav error("Port forwarding is disabled to avoid " 1074af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1075af12a3e7SDag-Erling Smørgrav options.num_local_forwards = 1076af12a3e7SDag-Erling Smørgrav options.num_remote_forwards = 0; 1077d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1078ca3176e7SBrian Feldman } 1079333ee039SDag-Erling Smørgrav if (options.tun_open != SSH_TUNMODE_NO) { 1080333ee039SDag-Erling Smørgrav error("Tunnel forwarding is disabled to avoid " 1081333ee039SDag-Erling Smørgrav "man-in-the-middle attacks."); 1082333ee039SDag-Erling Smørgrav options.tun_open = SSH_TUNMODE_NO; 1083d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1084333ee039SDag-Erling Smørgrav } 1085d4af9e69SDag-Erling Smørgrav if (options.exit_on_forward_failure && cancelled_forwarding) 1086d4af9e69SDag-Erling Smørgrav fatal("Error: forwarding disabled due to host key " 1087d4af9e69SDag-Erling Smørgrav "check failure"); 1088d4af9e69SDag-Erling Smørgrav 1089e8aafc91SKris Kennaway /* 1090e8aafc91SKris Kennaway * XXX Should permit the user to change to use the new id. 1091e8aafc91SKris Kennaway * This could be done by converting the host key to an 1092e8aafc91SKris Kennaway * identifying sentence, tell that the host identifies itself 1093b15c8340SDag-Erling Smørgrav * by that sentence, and ask the user if he/she wishes to 1094e8aafc91SKris Kennaway * accept the authentication. 1095e8aafc91SKris Kennaway */ 1096e8aafc91SKris Kennaway break; 1097f388f5efSDag-Erling Smørgrav case HOST_FOUND: 1098f388f5efSDag-Erling Smørgrav fatal("internal error"); 1099f388f5efSDag-Erling Smørgrav break; 1100e8aafc91SKris Kennaway } 1101ca3176e7SBrian Feldman 1102ca3176e7SBrian Feldman if (options.check_host_ip && host_status != HOST_CHANGED && 1103ca3176e7SBrian Feldman ip_status == HOST_CHANGED) { 1104af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 1105af12a3e7SDag-Erling Smørgrav "Warning: the %s host key for '%.200s' " 1106af12a3e7SDag-Erling Smørgrav "differs from the key for the IP address '%.128s'" 11074a421b63SDag-Erling Smørgrav "\nOffending key for IP in %s:%lu", 11084a421b63SDag-Erling Smørgrav type, host, ip, ip_found->file, ip_found->line); 1109af12a3e7SDag-Erling Smørgrav if (host_status == HOST_OK) { 1110af12a3e7SDag-Erling Smørgrav len = strlen(msg); 1111af12a3e7SDag-Erling Smørgrav snprintf(msg + len, sizeof(msg) - len, 11124a421b63SDag-Erling Smørgrav "\nMatching host key in %s:%lu", 11134a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1114af12a3e7SDag-Erling Smørgrav } 1115ca3176e7SBrian Feldman if (options.strict_host_key_checking == 1) { 1116cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 1117af12a3e7SDag-Erling Smørgrav error("Exiting, you have requested strict checking."); 1118af12a3e7SDag-Erling Smørgrav goto fail; 1119ca3176e7SBrian Feldman } else if (options.strict_host_key_checking == 2) { 1120af12a3e7SDag-Erling Smørgrav strlcat(msg, "\nAre you sure you want " 1121af12a3e7SDag-Erling Smørgrav "to continue connecting (yes/no)? ", sizeof(msg)); 1122af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 1123af12a3e7SDag-Erling Smørgrav goto fail; 1124af12a3e7SDag-Erling Smørgrav } else { 1125cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 1126ca3176e7SBrian Feldman } 1127ca3176e7SBrian Feldman } 1128ca3176e7SBrian Feldman 1129e8aafc91SKris Kennaway xfree(ip); 1130333ee039SDag-Erling Smørgrav xfree(host); 11314a421b63SDag-Erling Smørgrav if (host_hostkeys != NULL) 11324a421b63SDag-Erling Smørgrav free_hostkeys(host_hostkeys); 11334a421b63SDag-Erling Smørgrav if (ip_hostkeys != NULL) 11344a421b63SDag-Erling Smørgrav free_hostkeys(ip_hostkeys); 1135af12a3e7SDag-Erling Smørgrav return 0; 1136af12a3e7SDag-Erling Smørgrav 1137af12a3e7SDag-Erling Smørgrav fail: 1138b15c8340SDag-Erling Smørgrav if (want_cert && host_status != HOST_REVOKED) { 1139b15c8340SDag-Erling Smørgrav /* 1140b15c8340SDag-Erling Smørgrav * No matching certificate. Downgrade cert to raw key and 1141b15c8340SDag-Erling Smørgrav * search normally. 1142b15c8340SDag-Erling Smørgrav */ 1143b15c8340SDag-Erling Smørgrav debug("No matching CA found. Retry with plain key"); 1144b15c8340SDag-Erling Smørgrav raw_key = key_from_private(host_key); 1145b15c8340SDag-Erling Smørgrav if (key_drop_cert(raw_key) != 0) 1146b15c8340SDag-Erling Smørgrav fatal("Couldn't drop certificate"); 1147b15c8340SDag-Erling Smørgrav host_key = raw_key; 1148b15c8340SDag-Erling Smørgrav goto retry; 1149b15c8340SDag-Erling Smørgrav } 1150b15c8340SDag-Erling Smørgrav if (raw_key != NULL) 1151b15c8340SDag-Erling Smørgrav key_free(raw_key); 1152af12a3e7SDag-Erling Smørgrav xfree(ip); 1153333ee039SDag-Erling Smørgrav xfree(host); 11544a421b63SDag-Erling Smørgrav if (host_hostkeys != NULL) 11554a421b63SDag-Erling Smørgrav free_hostkeys(host_hostkeys); 11564a421b63SDag-Erling Smørgrav if (ip_hostkeys != NULL) 11574a421b63SDag-Erling Smørgrav free_hostkeys(ip_hostkeys); 1158af12a3e7SDag-Erling Smørgrav return -1; 1159e8aafc91SKris Kennaway } 1160511b41d2SMark Murray 1161cf2b5f3bSDag-Erling Smørgrav /* returns 0 if key verifies or -1 if key does NOT verify */ 1162fe5fd017SMark Murray int 1163af12a3e7SDag-Erling Smørgrav verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 1164fe5fd017SMark Murray { 11651ec0d754SDag-Erling Smørgrav int flags = 0; 11664a421b63SDag-Erling Smørgrav char *fp; 11674a421b63SDag-Erling Smørgrav 11684a421b63SDag-Erling Smørgrav fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 11694a421b63SDag-Erling Smørgrav debug("Server host key: %s %s", key_type(host_key), fp); 11704a421b63SDag-Erling Smørgrav xfree(fp); 1171fe5fd017SMark Murray 1172b15c8340SDag-Erling Smørgrav /* XXX certs are not yet supported for DNS */ 1173b15c8340SDag-Erling Smørgrav if (!key_is_cert(host_key) && options.verify_host_key_dns && 11741ec0d754SDag-Erling Smørgrav verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { 11751ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_FOUND) { 11761ec0d754SDag-Erling Smørgrav 11771ec0d754SDag-Erling Smørgrav if (options.verify_host_key_dns == 1 && 11781ec0d754SDag-Erling Smørgrav flags & DNS_VERIFY_MATCH && 11791ec0d754SDag-Erling Smørgrav flags & DNS_VERIFY_SECURE) 1180cf2b5f3bSDag-Erling Smørgrav return 0; 11811ec0d754SDag-Erling Smørgrav 11821ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_MATCH) { 11831ec0d754SDag-Erling Smørgrav matching_host_key_dns = 1; 11841ec0d754SDag-Erling Smørgrav } else { 11851ec0d754SDag-Erling Smørgrav warn_changed_key(host_key); 11861ec0d754SDag-Erling Smørgrav error("Update the SSHFP RR in DNS with the new " 11871ec0d754SDag-Erling Smørgrav "host key to get rid of this message."); 1188cf2b5f3bSDag-Erling Smørgrav } 1189cf2b5f3bSDag-Erling Smørgrav } 11901ec0d754SDag-Erling Smørgrav } 1191cf2b5f3bSDag-Erling Smørgrav 1192e146993eSDag-Erling Smørgrav return check_host_key(host, hostaddr, options.port, host_key, RDRW, 1193e146993eSDag-Erling Smørgrav options.user_hostfiles, options.num_user_hostfiles, 1194e146993eSDag-Erling Smørgrav options.system_hostfiles, options.num_system_hostfiles); 1195fe5fd017SMark Murray } 1196fe5fd017SMark Murray 1197511b41d2SMark Murray /* 1198511b41d2SMark Murray * Starts a dialog with the server, and authenticates the current user on the 1199511b41d2SMark Murray * server. This does not need any extra privileges. The basic connection 1200511b41d2SMark Murray * to the server must already have been established before this is called. 1201511b41d2SMark Murray * If login fails, this function prints an error and never returns. 1202511b41d2SMark Murray * This function does not require super-user privileges. 1203511b41d2SMark Murray */ 1204511b41d2SMark Murray void 120580628bacSDag-Erling Smørgrav ssh_login(Sensitive *sensitive, const char *orighost, 12064a421b63SDag-Erling Smørgrav struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) 1207511b41d2SMark Murray { 1208511b41d2SMark Murray char *host, *cp; 1209e8aafc91SKris Kennaway char *server_user, *local_user; 1210e8aafc91SKris Kennaway 1211e8aafc91SKris Kennaway local_user = xstrdup(pw->pw_name); 1212e8aafc91SKris Kennaway server_user = options.user ? options.user : local_user; 1213511b41d2SMark Murray 1214511b41d2SMark Murray /* Convert the user-supplied hostname into all lowercase. */ 1215511b41d2SMark Murray host = xstrdup(orighost); 1216511b41d2SMark Murray for (cp = host; *cp; cp++) 1217511b41d2SMark Murray if (isupper(*cp)) 1218333ee039SDag-Erling Smørgrav *cp = (char)tolower(*cp); 1219511b41d2SMark Murray 1220511b41d2SMark Murray /* Exchange protocol version identification strings with the server. */ 1221d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(timeout_ms); 1222511b41d2SMark Murray 1223511b41d2SMark Murray /* Put the connection into non-blocking mode. */ 1224511b41d2SMark Murray packet_set_nonblocking(); 1225511b41d2SMark Murray 1226511b41d2SMark Murray /* key exchange */ 1227511b41d2SMark Murray /* authenticate user */ 1228e8aafc91SKris Kennaway if (compat20) { 12294a421b63SDag-Erling Smørgrav ssh_kex2(host, hostaddr, port); 123080628bacSDag-Erling Smørgrav ssh_userauth2(local_user, server_user, host, sensitive); 1231e8aafc91SKris Kennaway } else { 1232e8aafc91SKris Kennaway ssh_kex(host, hostaddr); 123380628bacSDag-Erling Smørgrav ssh_userauth1(local_user, server_user, host, sensitive); 1234e8aafc91SKris Kennaway } 1235333ee039SDag-Erling Smørgrav xfree(local_user); 1236511b41d2SMark Murray } 1237e0fbb1d2SBrian Feldman 1238e0fbb1d2SBrian Feldman void 1239e0fbb1d2SBrian Feldman ssh_put_password(char *password) 1240e0fbb1d2SBrian Feldman { 1241e0fbb1d2SBrian Feldman int size; 1242e0fbb1d2SBrian Feldman char *padded; 1243e0fbb1d2SBrian Feldman 1244ca3176e7SBrian Feldman if (datafellows & SSH_BUG_PASSWORDPAD) { 1245af12a3e7SDag-Erling Smørgrav packet_put_cstring(password); 1246ca3176e7SBrian Feldman return; 1247ca3176e7SBrian Feldman } 1248e0fbb1d2SBrian Feldman size = roundup(strlen(password) + 1, 32); 1249333ee039SDag-Erling Smørgrav padded = xcalloc(1, size); 1250e0fbb1d2SBrian Feldman strlcpy(padded, password, size); 1251e0fbb1d2SBrian Feldman packet_put_string(padded, size); 1252e0fbb1d2SBrian Feldman memset(padded, 0, size); 1253e0fbb1d2SBrian Feldman xfree(padded); 1254e0fbb1d2SBrian Feldman } 1255f388f5efSDag-Erling Smørgrav 1256f388f5efSDag-Erling Smørgrav /* print all known host keys for a given host, but skip keys of given type */ 1257f388f5efSDag-Erling Smørgrav static int 12584a421b63SDag-Erling Smørgrav show_other_keys(struct hostkeys *hostkeys, Key *key) 1259f388f5efSDag-Erling Smørgrav { 12604a421b63SDag-Erling Smørgrav int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1}; 12614a421b63SDag-Erling Smørgrav int i, ret = 0; 12624a421b63SDag-Erling Smørgrav char *fp, *ra; 12634a421b63SDag-Erling Smørgrav const struct hostkey_entry *found; 1264f388f5efSDag-Erling Smørgrav 1265f388f5efSDag-Erling Smørgrav for (i = 0; type[i] != -1; i++) { 1266f388f5efSDag-Erling Smørgrav if (type[i] == key->type) 1267f388f5efSDag-Erling Smørgrav continue; 12684a421b63SDag-Erling Smørgrav if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) 1269f388f5efSDag-Erling Smørgrav continue; 12704a421b63SDag-Erling Smørgrav fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX); 12714a421b63SDag-Erling Smørgrav ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART); 12724a421b63SDag-Erling Smørgrav logit("WARNING: %s key found for host %s\n" 12734a421b63SDag-Erling Smørgrav "in %s:%lu\n" 12744a421b63SDag-Erling Smørgrav "%s key fingerprint %s.", 12754a421b63SDag-Erling Smørgrav key_type(found->key), 12764a421b63SDag-Erling Smørgrav found->host, found->file, found->line, 12774a421b63SDag-Erling Smørgrav key_type(found->key), fp); 12784a421b63SDag-Erling Smørgrav if (options.visual_host_key) 12794a421b63SDag-Erling Smørgrav logit("%s", ra); 12804a421b63SDag-Erling Smørgrav xfree(ra); 12814a421b63SDag-Erling Smørgrav xfree(fp); 12824a421b63SDag-Erling Smørgrav ret = 1; 1283f388f5efSDag-Erling Smørgrav } 12844a421b63SDag-Erling Smørgrav return ret; 1285f388f5efSDag-Erling Smørgrav } 12861ec0d754SDag-Erling Smørgrav 12871ec0d754SDag-Erling Smørgrav static void 12881ec0d754SDag-Erling Smørgrav warn_changed_key(Key *host_key) 12891ec0d754SDag-Erling Smørgrav { 12901ec0d754SDag-Erling Smørgrav char *fp; 12911ec0d754SDag-Erling Smørgrav 12921ec0d754SDag-Erling Smørgrav fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 12931ec0d754SDag-Erling Smørgrav 12941ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 12951ec0d754SDag-Erling Smørgrav error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 12961ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 12971ec0d754SDag-Erling Smørgrav error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 12981ec0d754SDag-Erling Smørgrav error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 12994a421b63SDag-Erling Smørgrav error("It is also possible that a host key has just been changed."); 13001ec0d754SDag-Erling Smørgrav error("The fingerprint for the %s key sent by the remote host is\n%s.", 13014a421b63SDag-Erling Smørgrav key_type(host_key), fp); 13021ec0d754SDag-Erling Smørgrav error("Please contact your system administrator."); 13031ec0d754SDag-Erling Smørgrav 13041ec0d754SDag-Erling Smørgrav xfree(fp); 13051ec0d754SDag-Erling Smørgrav } 1306b74df5b2SDag-Erling Smørgrav 1307b74df5b2SDag-Erling Smørgrav /* 1308b74df5b2SDag-Erling Smørgrav * Execute a local command 1309b74df5b2SDag-Erling Smørgrav */ 1310b74df5b2SDag-Erling Smørgrav int 1311b74df5b2SDag-Erling Smørgrav ssh_local_cmd(const char *args) 1312b74df5b2SDag-Erling Smørgrav { 1313b74df5b2SDag-Erling Smørgrav char *shell; 1314b74df5b2SDag-Erling Smørgrav pid_t pid; 1315b74df5b2SDag-Erling Smørgrav int status; 13164a421b63SDag-Erling Smørgrav void (*osighand)(int); 1317b74df5b2SDag-Erling Smørgrav 1318b74df5b2SDag-Erling Smørgrav if (!options.permit_local_command || 1319b74df5b2SDag-Erling Smørgrav args == NULL || !*args) 1320b74df5b2SDag-Erling Smørgrav return (1); 1321b74df5b2SDag-Erling Smørgrav 13224a421b63SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 1323b74df5b2SDag-Erling Smørgrav shell = _PATH_BSHELL; 1324b74df5b2SDag-Erling Smørgrav 13254a421b63SDag-Erling Smørgrav osighand = signal(SIGCHLD, SIG_DFL); 1326b74df5b2SDag-Erling Smørgrav pid = fork(); 1327b74df5b2SDag-Erling Smørgrav if (pid == 0) { 13284a421b63SDag-Erling Smørgrav signal(SIGPIPE, SIG_DFL); 1329b74df5b2SDag-Erling Smørgrav debug3("Executing %s -c \"%s\"", shell, args); 1330b74df5b2SDag-Erling Smørgrav execl(shell, shell, "-c", args, (char *)NULL); 1331b74df5b2SDag-Erling Smørgrav error("Couldn't execute %s -c \"%s\": %s", 1332b74df5b2SDag-Erling Smørgrav shell, args, strerror(errno)); 1333b74df5b2SDag-Erling Smørgrav _exit(1); 1334b74df5b2SDag-Erling Smørgrav } else if (pid == -1) 1335b74df5b2SDag-Erling Smørgrav fatal("fork failed: %.100s", strerror(errno)); 1336b74df5b2SDag-Erling Smørgrav while (waitpid(pid, &status, 0) == -1) 1337b74df5b2SDag-Erling Smørgrav if (errno != EINTR) 1338b74df5b2SDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 13394a421b63SDag-Erling Smørgrav signal(SIGCHLD, osighand); 1340b74df5b2SDag-Erling Smørgrav 1341b74df5b2SDag-Erling Smørgrav if (!WIFEXITED(status)) 1342b74df5b2SDag-Erling Smørgrav return (1); 1343b74df5b2SDag-Erling Smørgrav 1344b74df5b2SDag-Erling Smørgrav return (WEXITSTATUS(status)); 1345b74df5b2SDag-Erling Smørgrav } 1346