1*4a421b63SDag-Erling Smørgrav /* $OpenBSD: sshconnect.c,v 1.232 2011/01/16 11:50:36 djm Exp $ */ 2d4af9e69SDag-Erling Smørgrav /* $FreeBSD$ */ 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> 38*4a421b63SDag-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 71*4a421b63SDag-Erling Smørgrav static pid_t proxy_command_pid = 0; 72*4a421b63SDag-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 79*4a421b63SDag-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*4a421b63SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 94d4af9e69SDag-Erling Smørgrav shell = _PATH_BSHELL; 95511b41d2SMark Murray 96511b41d2SMark Murray /* Convert the port number into a string. */ 97511b41d2SMark Murray snprintf(strport, sizeof strport, "%hu", port); 98511b41d2SMark Murray 99f388f5efSDag-Erling Smørgrav /* 100f388f5efSDag-Erling Smørgrav * Build the final command string in the buffer by making the 101f388f5efSDag-Erling Smørgrav * appropriate substitutions to the given proxy command. 102f388f5efSDag-Erling Smørgrav * 103f388f5efSDag-Erling Smørgrav * Use "exec" to avoid "sh -c" processes on some platforms 104f388f5efSDag-Erling Smørgrav * (e.g. Solaris) 105f388f5efSDag-Erling Smørgrav */ 106333ee039SDag-Erling Smørgrav xasprintf(&tmp, "exec %s", proxy_command); 107e2f6069cSDag-Erling Smørgrav command_string = percent_expand(tmp, "h", host, "p", strport, 108e2f6069cSDag-Erling Smørgrav "r", options.user, (char *)NULL); 109d4ecd108SDag-Erling Smørgrav xfree(tmp); 110511b41d2SMark Murray 111511b41d2SMark Murray /* Create pipes for communicating with the proxy. */ 112511b41d2SMark Murray if (pipe(pin) < 0 || pipe(pout) < 0) 113511b41d2SMark Murray fatal("Could not create pipes to communicate with the proxy: %.100s", 114511b41d2SMark Murray strerror(errno)); 115511b41d2SMark Murray 116511b41d2SMark Murray debug("Executing proxy command: %.500s", command_string); 117511b41d2SMark Murray 118511b41d2SMark Murray /* Fork and execute the proxy command. */ 119511b41d2SMark Murray if ((pid = fork()) == 0) { 120511b41d2SMark Murray char *argv[10]; 121511b41d2SMark Murray 122511b41d2SMark Murray /* Child. Permanently give up superuser privileges. */ 123333ee039SDag-Erling Smørgrav permanently_drop_suid(original_real_uid); 124511b41d2SMark Murray 125511b41d2SMark Murray /* Redirect stdin and stdout. */ 126511b41d2SMark Murray close(pin[1]); 127511b41d2SMark Murray if (pin[0] != 0) { 128511b41d2SMark Murray if (dup2(pin[0], 0) < 0) 129511b41d2SMark Murray perror("dup2 stdin"); 130511b41d2SMark Murray close(pin[0]); 131511b41d2SMark Murray } 132511b41d2SMark Murray close(pout[0]); 133511b41d2SMark Murray if (dup2(pout[1], 1) < 0) 134511b41d2SMark Murray perror("dup2 stdout"); 135511b41d2SMark Murray /* Cannot be 1 because pin allocated two descriptors. */ 136511b41d2SMark Murray close(pout[1]); 137511b41d2SMark Murray 138511b41d2SMark Murray /* Stderr is left as it is so that error messages get 139511b41d2SMark Murray printed on the user's terminal. */ 140d4af9e69SDag-Erling Smørgrav argv[0] = shell; 141511b41d2SMark Murray argv[1] = "-c"; 142511b41d2SMark Murray argv[2] = command_string; 143511b41d2SMark Murray argv[3] = NULL; 144511b41d2SMark Murray 145511b41d2SMark Murray /* Execute the proxy command. Note that we gave up any 146511b41d2SMark Murray extra privileges above. */ 147*4a421b63SDag-Erling Smørgrav signal(SIGPIPE, SIG_DFL); 148ca3176e7SBrian Feldman execv(argv[0], argv); 149ca3176e7SBrian Feldman perror(argv[0]); 150511b41d2SMark Murray exit(1); 151511b41d2SMark Murray } 152511b41d2SMark Murray /* Parent. */ 153511b41d2SMark Murray if (pid < 0) 154511b41d2SMark Murray fatal("fork failed: %.100s", strerror(errno)); 155f388f5efSDag-Erling Smørgrav else 156f388f5efSDag-Erling Smørgrav proxy_command_pid = pid; /* save pid to clean up later */ 157511b41d2SMark Murray 158511b41d2SMark Murray /* Close child side of the descriptors. */ 159511b41d2SMark Murray close(pin[0]); 160511b41d2SMark Murray close(pout[1]); 161511b41d2SMark Murray 162511b41d2SMark Murray /* Free the command name. */ 163d4ecd108SDag-Erling Smørgrav xfree(command_string); 164511b41d2SMark Murray 165511b41d2SMark Murray /* Set the connection file descriptors. */ 166511b41d2SMark Murray packet_set_connection(pout[0], pin[1]); 167d4af9e69SDag-Erling Smørgrav packet_set_timeout(options.server_alive_interval, 168d4af9e69SDag-Erling Smørgrav options.server_alive_count_max); 169511b41d2SMark Murray 170af12a3e7SDag-Erling Smørgrav /* Indicate OK return */ 171af12a3e7SDag-Erling Smørgrav return 0; 172511b41d2SMark Murray } 173511b41d2SMark Murray 174*4a421b63SDag-Erling Smørgrav void 175*4a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(void) 176*4a421b63SDag-Erling Smørgrav { 177*4a421b63SDag-Erling Smørgrav /* 178*4a421b63SDag-Erling Smørgrav * Send SIGHUP to proxy command if used. We don't wait() in 179*4a421b63SDag-Erling Smørgrav * case it hangs and instead rely on init to reap the child 180*4a421b63SDag-Erling Smørgrav */ 181*4a421b63SDag-Erling Smørgrav if (proxy_command_pid > 1) 182*4a421b63SDag-Erling Smørgrav kill(proxy_command_pid, SIGHUP); 183*4a421b63SDag-Erling Smørgrav } 184*4a421b63SDag-Erling Smørgrav 185511b41d2SMark Murray /* 186511b41d2SMark Murray * Creates a (possibly privileged) socket for use as the ssh connection. 187511b41d2SMark Murray */ 188af12a3e7SDag-Erling Smørgrav static int 189cf2b5f3bSDag-Erling Smørgrav ssh_create_socket(int privileged, struct addrinfo *ai) 190511b41d2SMark Murray { 191af12a3e7SDag-Erling Smørgrav int sock, gaierr; 192af12a3e7SDag-Erling Smørgrav struct addrinfo hints, *res; 193511b41d2SMark Murray 194511b41d2SMark Murray /* 195511b41d2SMark Murray * If we are running as root and want to connect to a privileged 196511b41d2SMark Murray * port, bind our own socket to a privileged port. 197511b41d2SMark Murray */ 198511b41d2SMark Murray if (privileged) { 199511b41d2SMark Murray int p = IPPORT_RESERVED - 1; 20080628bacSDag-Erling Smørgrav PRIV_START; 201cf2b5f3bSDag-Erling Smørgrav sock = rresvport_af(&p, ai->ai_family); 20280628bacSDag-Erling Smørgrav PRIV_END; 203511b41d2SMark Murray if (sock < 0) 204cf2b5f3bSDag-Erling Smørgrav error("rresvport: af=%d %.100s", ai->ai_family, 205cf2b5f3bSDag-Erling Smørgrav strerror(errno)); 206511b41d2SMark Murray else 207511b41d2SMark Murray debug("Allocated local port %d.", p); 208af12a3e7SDag-Erling Smørgrav return sock; 209af12a3e7SDag-Erling Smørgrav } 210cf2b5f3bSDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 211b15c8340SDag-Erling Smørgrav if (sock < 0) { 212511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 213b15c8340SDag-Erling Smørgrav return -1; 214b15c8340SDag-Erling Smørgrav } 215b15c8340SDag-Erling Smørgrav fcntl(sock, F_SETFD, FD_CLOEXEC); 216af12a3e7SDag-Erling Smørgrav 217af12a3e7SDag-Erling Smørgrav /* Bind the socket to an alternative local IP address */ 218af12a3e7SDag-Erling Smørgrav if (options.bind_address == NULL) 219af12a3e7SDag-Erling Smørgrav return sock; 220af12a3e7SDag-Erling Smørgrav 221af12a3e7SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 222cf2b5f3bSDag-Erling Smørgrav hints.ai_family = ai->ai_family; 223cf2b5f3bSDag-Erling Smørgrav hints.ai_socktype = ai->ai_socktype; 224cf2b5f3bSDag-Erling Smørgrav hints.ai_protocol = ai->ai_protocol; 225af12a3e7SDag-Erling Smørgrav hints.ai_flags = AI_PASSIVE; 226d4af9e69SDag-Erling Smørgrav gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); 227af12a3e7SDag-Erling Smørgrav if (gaierr) { 228af12a3e7SDag-Erling Smørgrav error("getaddrinfo: %s: %s", options.bind_address, 229d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 230af12a3e7SDag-Erling Smørgrav close(sock); 231af12a3e7SDag-Erling Smørgrav return -1; 232511b41d2SMark Murray } 233af12a3e7SDag-Erling Smørgrav if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 234af12a3e7SDag-Erling Smørgrav error("bind: %s: %s", options.bind_address, strerror(errno)); 235af12a3e7SDag-Erling Smørgrav close(sock); 236af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 237af12a3e7SDag-Erling Smørgrav return -1; 238af12a3e7SDag-Erling Smørgrav } 239af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 240511b41d2SMark Murray return sock; 241511b41d2SMark Murray } 242511b41d2SMark Murray 243cf2b5f3bSDag-Erling Smørgrav static int 244cf2b5f3bSDag-Erling Smørgrav timeout_connect(int sockfd, const struct sockaddr *serv_addr, 245d4af9e69SDag-Erling Smørgrav socklen_t addrlen, int *timeoutp) 246cf2b5f3bSDag-Erling Smørgrav { 247cf2b5f3bSDag-Erling Smørgrav fd_set *fdset; 248d4af9e69SDag-Erling Smørgrav struct timeval tv, t_start; 249cf2b5f3bSDag-Erling Smørgrav socklen_t optlen; 250333ee039SDag-Erling Smørgrav int optval, rc, result = -1; 251cf2b5f3bSDag-Erling Smørgrav 252d4af9e69SDag-Erling Smørgrav gettimeofday(&t_start, NULL); 253d4af9e69SDag-Erling Smørgrav 254d4af9e69SDag-Erling Smørgrav if (*timeoutp <= 0) { 255d4af9e69SDag-Erling Smørgrav result = connect(sockfd, serv_addr, addrlen); 256d4af9e69SDag-Erling Smørgrav goto done; 257d4af9e69SDag-Erling Smørgrav } 258cf2b5f3bSDag-Erling Smørgrav 2591ec0d754SDag-Erling Smørgrav set_nonblock(sockfd); 260cf2b5f3bSDag-Erling Smørgrav rc = connect(sockfd, serv_addr, addrlen); 2611ec0d754SDag-Erling Smørgrav if (rc == 0) { 2621ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 263d4af9e69SDag-Erling Smørgrav result = 0; 264d4af9e69SDag-Erling Smørgrav goto done; 2651ec0d754SDag-Erling Smørgrav } 266d4af9e69SDag-Erling Smørgrav if (errno != EINPROGRESS) { 267d4af9e69SDag-Erling Smørgrav result = -1; 268d4af9e69SDag-Erling Smørgrav goto done; 269d4af9e69SDag-Erling Smørgrav } 270cf2b5f3bSDag-Erling Smørgrav 271333ee039SDag-Erling Smørgrav fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), 272333ee039SDag-Erling Smørgrav sizeof(fd_mask)); 273cf2b5f3bSDag-Erling Smørgrav FD_SET(sockfd, fdset); 274d4af9e69SDag-Erling Smørgrav ms_to_timeval(&tv, *timeoutp); 275cf2b5f3bSDag-Erling Smørgrav 276cf2b5f3bSDag-Erling Smørgrav for (;;) { 277cf2b5f3bSDag-Erling Smørgrav rc = select(sockfd + 1, NULL, fdset, NULL, &tv); 278cf2b5f3bSDag-Erling Smørgrav if (rc != -1 || errno != EINTR) 279cf2b5f3bSDag-Erling Smørgrav break; 280cf2b5f3bSDag-Erling Smørgrav } 281cf2b5f3bSDag-Erling Smørgrav 282cf2b5f3bSDag-Erling Smørgrav switch (rc) { 283cf2b5f3bSDag-Erling Smørgrav case 0: 284cf2b5f3bSDag-Erling Smørgrav /* Timed out */ 285cf2b5f3bSDag-Erling Smørgrav errno = ETIMEDOUT; 286cf2b5f3bSDag-Erling Smørgrav break; 287cf2b5f3bSDag-Erling Smørgrav case -1: 288cf2b5f3bSDag-Erling Smørgrav /* Select error */ 289cf2b5f3bSDag-Erling Smørgrav debug("select: %s", strerror(errno)); 290cf2b5f3bSDag-Erling Smørgrav break; 291cf2b5f3bSDag-Erling Smørgrav case 1: 292cf2b5f3bSDag-Erling Smørgrav /* Completed or failed */ 293cf2b5f3bSDag-Erling Smørgrav optval = 0; 294cf2b5f3bSDag-Erling Smørgrav optlen = sizeof(optval); 295cf2b5f3bSDag-Erling Smørgrav if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, 296cf2b5f3bSDag-Erling Smørgrav &optlen) == -1) { 297cf2b5f3bSDag-Erling Smørgrav debug("getsockopt: %s", strerror(errno)); 298cf2b5f3bSDag-Erling Smørgrav break; 299cf2b5f3bSDag-Erling Smørgrav } 300cf2b5f3bSDag-Erling Smørgrav if (optval != 0) { 301cf2b5f3bSDag-Erling Smørgrav errno = optval; 302cf2b5f3bSDag-Erling Smørgrav break; 303cf2b5f3bSDag-Erling Smørgrav } 304cf2b5f3bSDag-Erling Smørgrav result = 0; 3051ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 306cf2b5f3bSDag-Erling Smørgrav break; 307cf2b5f3bSDag-Erling Smørgrav default: 308cf2b5f3bSDag-Erling Smørgrav /* Should not occur */ 309cf2b5f3bSDag-Erling Smørgrav fatal("Bogus return (%d) from select()", rc); 310cf2b5f3bSDag-Erling Smørgrav } 311cf2b5f3bSDag-Erling Smørgrav 312cf2b5f3bSDag-Erling Smørgrav xfree(fdset); 313d4af9e69SDag-Erling Smørgrav 314d4af9e69SDag-Erling Smørgrav done: 315d4af9e69SDag-Erling Smørgrav if (result == 0 && *timeoutp > 0) { 316d4af9e69SDag-Erling Smørgrav ms_subtract_diff(&t_start, timeoutp); 317d4af9e69SDag-Erling Smørgrav if (*timeoutp <= 0) { 318d4af9e69SDag-Erling Smørgrav errno = ETIMEDOUT; 319d4af9e69SDag-Erling Smørgrav result = -1; 320d4af9e69SDag-Erling Smørgrav } 321d4af9e69SDag-Erling Smørgrav } 322d4af9e69SDag-Erling Smørgrav 323cf2b5f3bSDag-Erling Smørgrav return (result); 324cf2b5f3bSDag-Erling Smørgrav } 325cf2b5f3bSDag-Erling Smørgrav 326511b41d2SMark Murray /* 327511b41d2SMark Murray * Opens a TCP/IP connection to the remote server on the given host. 328511b41d2SMark Murray * The address of the remote host will be returned in hostaddr. 32980628bacSDag-Erling Smørgrav * If port is 0, the default port will be used. If needpriv is true, 330511b41d2SMark Murray * a privileged port will be allocated to make the connection. 33180628bacSDag-Erling Smørgrav * This requires super-user privileges if needpriv is true. 332511b41d2SMark Murray * Connection_attempts specifies the maximum number of tries (one per 333511b41d2SMark Murray * second). If proxy_command is non-NULL, it specifies the command (with %h 334511b41d2SMark Murray * and %p substituted for host and port, respectively) to use to contact 335511b41d2SMark Murray * the daemon. 336511b41d2SMark Murray */ 337511b41d2SMark Murray int 3381f5ce8f4SBrian Feldman ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 339d4af9e69SDag-Erling Smørgrav u_short port, int family, int connection_attempts, int *timeout_ms, 340d4af9e69SDag-Erling Smørgrav int want_keepalive, int needpriv, const char *proxy_command) 341511b41d2SMark Murray { 342511b41d2SMark Murray int gaierr; 343ca3176e7SBrian Feldman int on = 1; 344ca3176e7SBrian Feldman int sock = -1, attempt; 345ca3176e7SBrian Feldman char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 346ca3176e7SBrian Feldman struct addrinfo hints, *ai, *aitop; 347511b41d2SMark Murray 348e73e9afaSDag-Erling Smørgrav debug2("ssh_connect: needpriv %d", needpriv); 349511b41d2SMark Murray 350511b41d2SMark Murray /* If a proxy command is given, connect using it. */ 351511b41d2SMark Murray if (proxy_command != NULL) 35280628bacSDag-Erling Smørgrav return ssh_proxy_connect(host, port, proxy_command); 353511b41d2SMark Murray 354511b41d2SMark Murray /* No proxy command. */ 355511b41d2SMark Murray 356511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 357af12a3e7SDag-Erling Smørgrav hints.ai_family = family; 358511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 359a82e551fSDag-Erling Smørgrav snprintf(strport, sizeof strport, "%u", port); 3601f5ce8f4SBrian Feldman if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 361d4af9e69SDag-Erling Smørgrav fatal("%s: Could not resolve hostname %.100s: %s", __progname, 362d4af9e69SDag-Erling Smørgrav host, ssh_gai_strerror(gaierr)); 363511b41d2SMark Murray 364333ee039SDag-Erling Smørgrav for (attempt = 0; attempt < connection_attempts; attempt++) { 36562efe23aSDag-Erling Smørgrav if (attempt > 0) { 36662efe23aSDag-Erling Smørgrav /* Sleep a moment before retrying. */ 36762efe23aSDag-Erling Smørgrav sleep(1); 368511b41d2SMark Murray debug("Trying again..."); 36962efe23aSDag-Erling Smørgrav } 370333ee039SDag-Erling Smørgrav /* 371333ee039SDag-Erling Smørgrav * Loop through addresses for this host, and try each one in 372333ee039SDag-Erling Smørgrav * sequence until the connection succeeds. 373333ee039SDag-Erling Smørgrav */ 374511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 375511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 376511b41d2SMark Murray continue; 377511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 378511b41d2SMark Murray ntop, sizeof(ntop), strport, sizeof(strport), 379511b41d2SMark Murray NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 380511b41d2SMark Murray error("ssh_connect: getnameinfo failed"); 381511b41d2SMark Murray continue; 382511b41d2SMark Murray } 383511b41d2SMark Murray debug("Connecting to %.200s [%.100s] port %s.", 3841f5ce8f4SBrian Feldman host, ntop, strport); 385511b41d2SMark Murray 386511b41d2SMark Murray /* Create a socket for connecting. */ 387cf2b5f3bSDag-Erling Smørgrav sock = ssh_create_socket(needpriv, ai); 388511b41d2SMark Murray if (sock < 0) 389af12a3e7SDag-Erling Smørgrav /* Any error is already output */ 390511b41d2SMark Murray continue; 391511b41d2SMark Murray 392cf2b5f3bSDag-Erling Smørgrav if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 393d4af9e69SDag-Erling Smørgrav timeout_ms) >= 0) { 394511b41d2SMark Murray /* Successful connection. */ 395c322fe35SKris Kennaway memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 396511b41d2SMark Murray break; 397511b41d2SMark Murray } else { 398f388f5efSDag-Erling Smørgrav debug("connect to address %s port %s: %s", 399f388f5efSDag-Erling Smørgrav ntop, strport, strerror(errno)); 400511b41d2SMark Murray close(sock); 401333ee039SDag-Erling Smørgrav sock = -1; 402511b41d2SMark Murray } 403511b41d2SMark Murray } 404333ee039SDag-Erling Smørgrav if (sock != -1) 405511b41d2SMark Murray break; /* Successful connection. */ 406511b41d2SMark Murray } 407511b41d2SMark Murray 408511b41d2SMark Murray freeaddrinfo(aitop); 409511b41d2SMark Murray 410511b41d2SMark Murray /* Return failure if we didn't get a successful connection. */ 411333ee039SDag-Erling Smørgrav if (sock == -1) { 412aa49c926SDag-Erling Smørgrav error("ssh: connect to host %s port %s: %s", 413f388f5efSDag-Erling Smørgrav host, strport, strerror(errno)); 414aa49c926SDag-Erling Smørgrav return (-1); 415f388f5efSDag-Erling Smørgrav } 416511b41d2SMark Murray 417511b41d2SMark Murray debug("Connection established."); 418511b41d2SMark Murray 4191ec0d754SDag-Erling Smørgrav /* Set SO_KEEPALIVE if requested. */ 420d4af9e69SDag-Erling Smørgrav if (want_keepalive && 421ca3176e7SBrian Feldman setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 422ca3176e7SBrian Feldman sizeof(on)) < 0) 423ca3176e7SBrian Feldman error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 424ca3176e7SBrian Feldman 425511b41d2SMark Murray /* Set the connection. */ 426511b41d2SMark Murray packet_set_connection(sock, sock); 427d4af9e69SDag-Erling Smørgrav packet_set_timeout(options.server_alive_interval, 428d4af9e69SDag-Erling Smørgrav options.server_alive_count_max); 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 */ 4377aee6ffeSDag-Erling Smørgrav void 438d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(int timeout_ms) 439511b41d2SMark Murray { 440e8aafc91SKris Kennaway char buf[256], remote_version[256]; /* must be same size! */ 441d4ecd108SDag-Erling Smørgrav int remote_major, remote_minor, 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; 445333ee039SDag-Erling Smørgrav u_int i, n; 446d4af9e69SDag-Erling Smørgrav size_t len; 447d4af9e69SDag-Erling Smørgrav int fdsetsz, remaining, rc; 448d4af9e69SDag-Erling Smørgrav struct timeval t_start, t_remaining; 449d4af9e69SDag-Erling Smørgrav fd_set *fdset; 450d4af9e69SDag-Erling Smørgrav 451d4af9e69SDag-Erling Smørgrav fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); 452d4af9e69SDag-Erling Smørgrav fdset = xcalloc(1, fdsetsz); 453511b41d2SMark Murray 454d4ecd108SDag-Erling Smørgrav /* Read other side's version identification. */ 455d4af9e69SDag-Erling Smørgrav remaining = timeout_ms; 456333ee039SDag-Erling Smørgrav for (n = 0;;) { 457e8aafc91SKris Kennaway for (i = 0; i < sizeof(buf) - 1; i++) { 458d4af9e69SDag-Erling Smørgrav if (timeout_ms > 0) { 459d4af9e69SDag-Erling Smørgrav gettimeofday(&t_start, NULL); 460d4af9e69SDag-Erling Smørgrav ms_to_timeval(&t_remaining, remaining); 461d4af9e69SDag-Erling Smørgrav FD_SET(connection_in, fdset); 462d4af9e69SDag-Erling Smørgrav rc = select(connection_in + 1, fdset, NULL, 463d4af9e69SDag-Erling Smørgrav fdset, &t_remaining); 464d4af9e69SDag-Erling Smørgrav ms_subtract_diff(&t_start, &remaining); 465d4af9e69SDag-Erling Smørgrav if (rc == 0 || remaining <= 0) 466d4af9e69SDag-Erling Smørgrav fatal("Connection timed out during " 467d4af9e69SDag-Erling Smørgrav "banner exchange"); 468d4af9e69SDag-Erling Smørgrav if (rc == -1) { 469d4af9e69SDag-Erling Smørgrav if (errno == EINTR) 470d4af9e69SDag-Erling Smørgrav continue; 471d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 472d4af9e69SDag-Erling Smørgrav "select: %s", strerror(errno)); 473d4af9e69SDag-Erling Smørgrav } 474d4af9e69SDag-Erling Smørgrav } 475d4af9e69SDag-Erling Smørgrav 4767aee6ffeSDag-Erling Smørgrav len = roaming_atomicio(read, connection_in, &buf[i], 1); 477d4ecd108SDag-Erling Smørgrav 478d4ecd108SDag-Erling Smørgrav if (len != 1 && errno == EPIPE) 479d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 480d4af9e69SDag-Erling Smørgrav "Connection closed by remote host"); 481d4ecd108SDag-Erling Smørgrav else if (len != 1) 482d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 483d4af9e69SDag-Erling Smørgrav "read: %.100s", strerror(errno)); 484e8aafc91SKris Kennaway if (buf[i] == '\r') { 485e8aafc91SKris Kennaway buf[i] = '\n'; 486e8aafc91SKris Kennaway buf[i + 1] = 0; 487e8aafc91SKris Kennaway continue; /**XXX wait for \n */ 488511b41d2SMark Murray } 489e8aafc91SKris Kennaway if (buf[i] == '\n') { 490e8aafc91SKris Kennaway buf[i + 1] = 0; 491511b41d2SMark Murray break; 492e8aafc91SKris Kennaway } 493333ee039SDag-Erling Smørgrav if (++n > 65536) 494d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 495d4af9e69SDag-Erling Smørgrav "No banner received"); 496e8aafc91SKris Kennaway } 497e8aafc91SKris Kennaway buf[sizeof(buf) - 1] = 0; 498c2d3a559SKris Kennaway if (strncmp(buf, "SSH-", 4) == 0) 499c2d3a559SKris Kennaway break; 500c2d3a559SKris Kennaway debug("ssh_exchange_identification: %s", buf); 501c2d3a559SKris Kennaway } 502e8aafc91SKris Kennaway server_version_string = xstrdup(buf); 503d4af9e69SDag-Erling Smørgrav xfree(fdset); 504511b41d2SMark Murray 505511b41d2SMark Murray /* 506e8aafc91SKris Kennaway * Check that the versions match. In future this might accept 507e8aafc91SKris Kennaway * several versions and set appropriate flags to handle them. 508511b41d2SMark Murray */ 509e8aafc91SKris Kennaway if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 510e8aafc91SKris Kennaway &remote_major, &remote_minor, remote_version) != 3) 511e8aafc91SKris Kennaway fatal("Bad remote protocol version identification: '%.100s'", buf); 512e8aafc91SKris Kennaway debug("Remote protocol version %d.%d, remote software version %.100s", 513e8aafc91SKris Kennaway remote_major, remote_minor, remote_version); 514511b41d2SMark Murray 515e8aafc91SKris Kennaway compat_datafellows(remote_version); 516e8aafc91SKris Kennaway mismatch = 0; 517e8aafc91SKris Kennaway 518e8aafc91SKris Kennaway switch (remote_major) { 519e8aafc91SKris Kennaway case 1: 520e8aafc91SKris Kennaway if (remote_minor == 99 && 521e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) && 522e8aafc91SKris Kennaway !(options.protocol & SSH_PROTO_1_PREFERRED)) { 523e8aafc91SKris Kennaway enable_compat20(); 524511b41d2SMark Murray break; 525e8aafc91SKris Kennaway } 526e8aafc91SKris Kennaway if (!(options.protocol & SSH_PROTO_1)) { 527e8aafc91SKris Kennaway mismatch = 1; 528e8aafc91SKris Kennaway break; 529e8aafc91SKris Kennaway } 530e8aafc91SKris Kennaway if (remote_minor < 3) { 531e8aafc91SKris Kennaway fatal("Remote machine has too old SSH software version."); 532ca3176e7SBrian Feldman } else if (remote_minor == 3 || remote_minor == 4) { 533e8aafc91SKris Kennaway /* We speak 1.3, too. */ 534e8aafc91SKris Kennaway enable_compat13(); 535ca3176e7SBrian Feldman minor1 = 3; 536e8aafc91SKris Kennaway if (options.forward_agent) { 537cf2b5f3bSDag-Erling Smørgrav logit("Agent forwarding disabled for protocol 1.3"); 538e8aafc91SKris Kennaway options.forward_agent = 0; 539e8aafc91SKris Kennaway } 540e8aafc91SKris Kennaway } 541e8aafc91SKris Kennaway break; 542e8aafc91SKris Kennaway case 2: 543e8aafc91SKris Kennaway if (options.protocol & SSH_PROTO_2) { 544e8aafc91SKris Kennaway enable_compat20(); 545e8aafc91SKris Kennaway break; 546e8aafc91SKris Kennaway } 547e8aafc91SKris Kennaway /* FALLTHROUGH */ 548511b41d2SMark Murray default: 549e8aafc91SKris Kennaway mismatch = 1; 550e8aafc91SKris Kennaway break; 551511b41d2SMark Murray } 552e8aafc91SKris Kennaway if (mismatch) 553e8aafc91SKris Kennaway fatal("Protocol major versions differ: %d vs. %d", 554e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 555e8aafc91SKris Kennaway remote_major); 556e8aafc91SKris Kennaway /* Send our own protocol version identification. */ 557d4af9e69SDag-Erling Smørgrav snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", 558e8aafc91SKris Kennaway compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 559ca3176e7SBrian Feldman compat20 ? PROTOCOL_MINOR_2 : minor1, 560d4af9e69SDag-Erling Smørgrav SSH_VERSION, compat20 ? "\r\n" : "\n"); 5617aee6ffeSDag-Erling Smørgrav if (roaming_atomicio(vwrite, connection_out, buf, strlen(buf)) 5627aee6ffeSDag-Erling Smørgrav != strlen(buf)) 563e8aafc91SKris Kennaway fatal("write: %.100s", strerror(errno)); 564e8aafc91SKris Kennaway client_version_string = xstrdup(buf); 565e8aafc91SKris Kennaway chop(client_version_string); 566e8aafc91SKris Kennaway chop(server_version_string); 567e8aafc91SKris Kennaway debug("Local version string %.100s", client_version_string); 568511b41d2SMark Murray } 569511b41d2SMark Murray 570ca3176e7SBrian Feldman /* defaults to 'no' */ 571af12a3e7SDag-Erling Smørgrav static int 572af12a3e7SDag-Erling Smørgrav confirm(const char *prompt) 573511b41d2SMark Murray { 574af12a3e7SDag-Erling Smørgrav const char *msg, *again = "Please type 'yes' or 'no': "; 575af12a3e7SDag-Erling Smørgrav char *p; 576af12a3e7SDag-Erling Smørgrav int ret = -1; 577511b41d2SMark Murray 578ca3176e7SBrian Feldman if (options.batch_mode) 579ca3176e7SBrian Feldman return 0; 580af12a3e7SDag-Erling Smørgrav for (msg = prompt;;msg = again) { 581af12a3e7SDag-Erling Smørgrav p = read_passphrase(msg, RP_ECHO); 582af12a3e7SDag-Erling Smørgrav if (p == NULL || 583af12a3e7SDag-Erling Smørgrav (p[0] == '\0') || (p[0] == '\n') || 584af12a3e7SDag-Erling Smørgrav strncasecmp(p, "no", 2) == 0) 585af12a3e7SDag-Erling Smørgrav ret = 0; 586f388f5efSDag-Erling Smørgrav if (p && strncasecmp(p, "yes", 3) == 0) 587af12a3e7SDag-Erling Smørgrav ret = 1; 588af12a3e7SDag-Erling Smørgrav if (p) 589af12a3e7SDag-Erling Smørgrav xfree(p); 590af12a3e7SDag-Erling Smørgrav if (ret != -1) 591af12a3e7SDag-Erling Smørgrav return ret; 592511b41d2SMark Murray } 593511b41d2SMark Murray } 594511b41d2SMark Murray 595b15c8340SDag-Erling Smørgrav static int 596b15c8340SDag-Erling Smørgrav check_host_cert(const char *host, const Key *host_key) 597b15c8340SDag-Erling Smørgrav { 598b15c8340SDag-Erling Smørgrav const char *reason; 599b15c8340SDag-Erling Smørgrav 600b15c8340SDag-Erling Smørgrav if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { 601b15c8340SDag-Erling Smørgrav error("%s", reason); 602b15c8340SDag-Erling Smørgrav return 0; 603b15c8340SDag-Erling Smørgrav } 604e2f6069cSDag-Erling Smørgrav if (buffer_len(&host_key->cert->critical) != 0) { 605e2f6069cSDag-Erling Smørgrav error("Certificate for %s contains unsupported " 606e2f6069cSDag-Erling Smørgrav "critical options(s)", host); 607b15c8340SDag-Erling Smørgrav return 0; 608b15c8340SDag-Erling Smørgrav } 609b15c8340SDag-Erling Smørgrav return 1; 610b15c8340SDag-Erling Smørgrav } 611b15c8340SDag-Erling Smørgrav 612*4a421b63SDag-Erling Smørgrav static int 613*4a421b63SDag-Erling Smørgrav sockaddr_is_local(struct sockaddr *hostaddr) 614*4a421b63SDag-Erling Smørgrav { 615*4a421b63SDag-Erling Smørgrav switch (hostaddr->sa_family) { 616*4a421b63SDag-Erling Smørgrav case AF_INET: 617*4a421b63SDag-Erling Smørgrav return (ntohl(((struct sockaddr_in *)hostaddr)-> 618*4a421b63SDag-Erling Smørgrav sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 619*4a421b63SDag-Erling Smørgrav case AF_INET6: 620*4a421b63SDag-Erling Smørgrav return IN6_IS_ADDR_LOOPBACK( 621*4a421b63SDag-Erling Smørgrav &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 622*4a421b63SDag-Erling Smørgrav default: 623*4a421b63SDag-Erling Smørgrav return 0; 624*4a421b63SDag-Erling Smørgrav } 625*4a421b63SDag-Erling Smørgrav } 626*4a421b63SDag-Erling Smørgrav 627*4a421b63SDag-Erling Smørgrav /* 628*4a421b63SDag-Erling Smørgrav * Prepare the hostname and ip address strings that are used to lookup 629*4a421b63SDag-Erling Smørgrav * host keys in known_hosts files. These may have a port number appended. 630*4a421b63SDag-Erling Smørgrav */ 631*4a421b63SDag-Erling Smørgrav void 632*4a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, 633*4a421b63SDag-Erling Smørgrav u_short port, char **hostfile_hostname, char **hostfile_ipaddr) 634*4a421b63SDag-Erling Smørgrav { 635*4a421b63SDag-Erling Smørgrav char ntop[NI_MAXHOST]; 636*4a421b63SDag-Erling Smørgrav socklen_t addrlen; 637*4a421b63SDag-Erling Smørgrav 638*4a421b63SDag-Erling Smørgrav switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { 639*4a421b63SDag-Erling Smørgrav case -1: 640*4a421b63SDag-Erling Smørgrav addrlen = 0; 641*4a421b63SDag-Erling Smørgrav break; 642*4a421b63SDag-Erling Smørgrav case AF_INET: 643*4a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr_in); 644*4a421b63SDag-Erling Smørgrav break; 645*4a421b63SDag-Erling Smørgrav case AF_INET6: 646*4a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr_in6); 647*4a421b63SDag-Erling Smørgrav break; 648*4a421b63SDag-Erling Smørgrav default: 649*4a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr); 650*4a421b63SDag-Erling Smørgrav break; 651*4a421b63SDag-Erling Smørgrav } 652*4a421b63SDag-Erling Smørgrav 653*4a421b63SDag-Erling Smørgrav /* 654*4a421b63SDag-Erling Smørgrav * We don't have the remote ip-address for connections 655*4a421b63SDag-Erling Smørgrav * using a proxy command 656*4a421b63SDag-Erling Smørgrav */ 657*4a421b63SDag-Erling Smørgrav if (hostfile_ipaddr != NULL) { 658*4a421b63SDag-Erling Smørgrav if (options.proxy_command == NULL) { 659*4a421b63SDag-Erling Smørgrav if (getnameinfo(hostaddr, addrlen, 660*4a421b63SDag-Erling Smørgrav ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) 661*4a421b63SDag-Erling Smørgrav fatal("check_host_key: getnameinfo failed"); 662*4a421b63SDag-Erling Smørgrav *hostfile_ipaddr = put_host_port(ntop, port); 663*4a421b63SDag-Erling Smørgrav } else { 664*4a421b63SDag-Erling Smørgrav *hostfile_ipaddr = xstrdup("<no hostip for proxy " 665*4a421b63SDag-Erling Smørgrav "command>"); 666*4a421b63SDag-Erling Smørgrav } 667*4a421b63SDag-Erling Smørgrav } 668*4a421b63SDag-Erling Smørgrav 669*4a421b63SDag-Erling Smørgrav /* 670*4a421b63SDag-Erling Smørgrav * Allow the user to record the key under a different name or 671*4a421b63SDag-Erling Smørgrav * differentiate a non-standard port. This is useful for ssh 672*4a421b63SDag-Erling Smørgrav * tunneling over forwarded connections or if you run multiple 673*4a421b63SDag-Erling Smørgrav * sshd's on different ports on the same machine. 674*4a421b63SDag-Erling Smørgrav */ 675*4a421b63SDag-Erling Smørgrav if (hostfile_hostname != NULL) { 676*4a421b63SDag-Erling Smørgrav if (options.host_key_alias != NULL) { 677*4a421b63SDag-Erling Smørgrav *hostfile_hostname = xstrdup(options.host_key_alias); 678*4a421b63SDag-Erling Smørgrav debug("using hostkeyalias: %s", *hostfile_hostname); 679*4a421b63SDag-Erling Smørgrav } else { 680*4a421b63SDag-Erling Smørgrav *hostfile_hostname = put_host_port(hostname, port); 681*4a421b63SDag-Erling Smørgrav } 682*4a421b63SDag-Erling Smørgrav } 683*4a421b63SDag-Erling Smørgrav } 684*4a421b63SDag-Erling Smørgrav 685e8aafc91SKris Kennaway /* 686af12a3e7SDag-Erling Smørgrav * check whether the supplied host key is valid, return -1 if the key 687af12a3e7SDag-Erling Smørgrav * is not valid. the user_hostfile will not be updated if 'readonly' is true. 688e8aafc91SKris Kennaway */ 689333ee039SDag-Erling Smørgrav #define RDRW 0 690333ee039SDag-Erling Smørgrav #define RDONLY 1 691333ee039SDag-Erling Smørgrav #define ROQUIET 2 692af12a3e7SDag-Erling Smørgrav static int 693333ee039SDag-Erling Smørgrav check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 694*4a421b63SDag-Erling Smørgrav Key *host_key, int readonly, char *user_hostfile, 695*4a421b63SDag-Erling Smørgrav char *system_hostfile) 696511b41d2SMark Murray { 697*4a421b63SDag-Erling Smørgrav Key *raw_key = NULL; 698b15c8340SDag-Erling Smørgrav const char *type; 699333ee039SDag-Erling Smørgrav char *ip = NULL, *host = NULL; 700d4af9e69SDag-Erling Smørgrav char hostline[1000], *hostp, *fp, *ra; 701e8aafc91SKris Kennaway HostStatus host_status; 702e8aafc91SKris Kennaway HostStatus ip_status; 703*4a421b63SDag-Erling Smørgrav int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; 704*4a421b63SDag-Erling Smørgrav int local = sockaddr_is_local(hostaddr); 705af12a3e7SDag-Erling Smørgrav char msg[1024]; 706*4a421b63SDag-Erling Smørgrav int len, cancelled_forwarding = 0; 707*4a421b63SDag-Erling Smørgrav struct hostkeys *host_hostkeys, *ip_hostkeys; 708*4a421b63SDag-Erling Smørgrav const struct hostkey_entry *host_found, *ip_found; 709511b41d2SMark Murray 710e8aafc91SKris Kennaway /* 711e8aafc91SKris Kennaway * Force accepting of the host key for loopback/localhost. The 712e8aafc91SKris Kennaway * problem is that if the home directory is NFS-mounted to multiple 713e8aafc91SKris Kennaway * machines, localhost will refer to a different machine in each of 714e8aafc91SKris Kennaway * them, and the user will get bogus HOST_CHANGED warnings. This 715e8aafc91SKris Kennaway * essentially disables host authentication for localhost; however, 716e8aafc91SKris Kennaway * this is probably not a real problem. 717e8aafc91SKris Kennaway */ 718af12a3e7SDag-Erling Smørgrav if (options.no_host_authentication_for_localhost == 1 && local && 719af12a3e7SDag-Erling Smørgrav options.host_key_alias == NULL) { 720ca3176e7SBrian Feldman debug("Forcing accepting of host key for " 721ca3176e7SBrian Feldman "loopback/localhost."); 722af12a3e7SDag-Erling Smørgrav return 0; 723511b41d2SMark Murray } 724511b41d2SMark Murray 725e8aafc91SKris Kennaway /* 726*4a421b63SDag-Erling Smørgrav * Prepare the hostname and address strings used for hostkey lookup. 727*4a421b63SDag-Erling Smørgrav * In some cases, these will have a port number appended. 728e8aafc91SKris Kennaway */ 729*4a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); 730d4af9e69SDag-Erling Smørgrav 731ca3176e7SBrian Feldman /* 732ca3176e7SBrian Feldman * Turn off check_host_ip if the connection is to localhost, via proxy 733ca3176e7SBrian Feldman * command or if we don't have a hostname to compare with 734ca3176e7SBrian Feldman */ 735333ee039SDag-Erling Smørgrav if (options.check_host_ip && (local || 736333ee039SDag-Erling Smørgrav strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 737ca3176e7SBrian Feldman options.check_host_ip = 0; 738ca3176e7SBrian Feldman 739*4a421b63SDag-Erling Smørgrav host_hostkeys = init_hostkeys(); 740*4a421b63SDag-Erling Smørgrav load_hostkeys(host_hostkeys, host, user_hostfile); 741*4a421b63SDag-Erling Smørgrav load_hostkeys(host_hostkeys, host, system_hostfile); 742*4a421b63SDag-Erling Smørgrav 743*4a421b63SDag-Erling Smørgrav ip_hostkeys = NULL; 744*4a421b63SDag-Erling Smørgrav if (!want_cert && options.check_host_ip) { 745*4a421b63SDag-Erling Smørgrav ip_hostkeys = init_hostkeys(); 746*4a421b63SDag-Erling Smørgrav load_hostkeys(ip_hostkeys, ip, user_hostfile); 747*4a421b63SDag-Erling Smørgrav load_hostkeys(ip_hostkeys, ip, system_hostfile); 748e8aafc91SKris Kennaway } 749e8aafc91SKris Kennaway 750b15c8340SDag-Erling Smørgrav retry: 751*4a421b63SDag-Erling Smørgrav /* Reload these as they may have changed on cert->key downgrade */ 752b15c8340SDag-Erling Smørgrav want_cert = key_is_cert(host_key); 753b15c8340SDag-Erling Smørgrav type = key_type(host_key); 754b15c8340SDag-Erling Smørgrav 755e8aafc91SKris Kennaway /* 756b74df5b2SDag-Erling Smørgrav * Check if the host key is present in the user's list of known 757e8aafc91SKris Kennaway * hosts or in the systemwide list. 758e8aafc91SKris Kennaway */ 759*4a421b63SDag-Erling Smørgrav host_status = check_key_in_hostkeys(host_hostkeys, host_key, 760*4a421b63SDag-Erling Smørgrav &host_found); 761*4a421b63SDag-Erling Smørgrav 762e8aafc91SKris Kennaway /* 763e8aafc91SKris Kennaway * Also perform check for the ip address, skip the check if we are 764b15c8340SDag-Erling Smørgrav * localhost, looking for a certificate, or the hostname was an ip 765b15c8340SDag-Erling Smørgrav * address to begin with. 766e8aafc91SKris Kennaway */ 767*4a421b63SDag-Erling Smørgrav if (!want_cert && ip_hostkeys != NULL) { 768*4a421b63SDag-Erling Smørgrav ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, 769*4a421b63SDag-Erling Smørgrav &ip_found); 770e8aafc91SKris Kennaway if (host_status == HOST_CHANGED && 771*4a421b63SDag-Erling Smørgrav (ip_status != HOST_CHANGED || 772*4a421b63SDag-Erling Smørgrav (ip_found != NULL && 773*4a421b63SDag-Erling Smørgrav !key_equal(ip_found->key, host_found->key)))) 774e8aafc91SKris Kennaway host_ip_differ = 1; 775e8aafc91SKris Kennaway } else 776e8aafc91SKris Kennaway ip_status = host_status; 777e8aafc91SKris Kennaway 778e8aafc91SKris Kennaway switch (host_status) { 779e8aafc91SKris Kennaway case HOST_OK: 780e8aafc91SKris Kennaway /* The host is known and the key matches. */ 781b15c8340SDag-Erling Smørgrav debug("Host '%.200s' is known and matches the %s host %s.", 782b15c8340SDag-Erling Smørgrav host, type, want_cert ? "certificate" : "key"); 783*4a421b63SDag-Erling Smørgrav debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", 784*4a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 785b15c8340SDag-Erling Smørgrav if (want_cert && !check_host_cert(hostname, host_key)) 786b15c8340SDag-Erling Smørgrav goto fail; 787ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 788b15c8340SDag-Erling Smørgrav if (readonly || want_cert) 789cf2b5f3bSDag-Erling Smørgrav logit("%s host key for IP address " 790af12a3e7SDag-Erling Smørgrav "'%.128s' not in list of known hosts.", 791e8aafc91SKris Kennaway type, ip); 792af12a3e7SDag-Erling Smørgrav else if (!add_host_to_hostfile(user_hostfile, ip, 793aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts)) 794cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the %s host key for IP " 795af12a3e7SDag-Erling Smørgrav "address '%.128s' to the list of known " 796af12a3e7SDag-Erling Smørgrav "hosts (%.30s).", type, ip, user_hostfile); 797af12a3e7SDag-Erling Smørgrav else 798cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added the %s host " 799af12a3e7SDag-Erling Smørgrav "key for IP address '%.128s' to the list " 800af12a3e7SDag-Erling Smørgrav "of known hosts.", type, ip); 801d4af9e69SDag-Erling Smørgrav } else if (options.visual_host_key) { 802d4af9e69SDag-Erling Smørgrav fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 803d4af9e69SDag-Erling Smørgrav ra = key_fingerprint(host_key, SSH_FP_MD5, 804d4af9e69SDag-Erling Smørgrav SSH_FP_RANDOMART); 805d4af9e69SDag-Erling Smørgrav logit("Host key fingerprint is %s\n%s\n", fp, ra); 806d4af9e69SDag-Erling Smørgrav xfree(ra); 807d4af9e69SDag-Erling Smørgrav xfree(fp); 808e8aafc91SKris Kennaway } 809e8aafc91SKris Kennaway break; 810e8aafc91SKris Kennaway case HOST_NEW: 811333ee039SDag-Erling Smørgrav if (options.host_key_alias == NULL && port != 0 && 812333ee039SDag-Erling Smørgrav port != SSH_DEFAULT_PORT) { 813333ee039SDag-Erling Smørgrav debug("checking without port identifier"); 814cce7d346SDag-Erling Smørgrav if (check_host_key(hostname, hostaddr, 0, host_key, 815cce7d346SDag-Erling Smørgrav ROQUIET, user_hostfile, system_hostfile) == 0) { 816333ee039SDag-Erling Smørgrav debug("found matching key w/out port"); 817333ee039SDag-Erling Smørgrav break; 818333ee039SDag-Erling Smørgrav } 819333ee039SDag-Erling Smørgrav } 820b15c8340SDag-Erling Smørgrav if (readonly || want_cert) 821af12a3e7SDag-Erling Smørgrav goto fail; 822e8aafc91SKris Kennaway /* The host is new. */ 823e8aafc91SKris Kennaway if (options.strict_host_key_checking == 1) { 824af12a3e7SDag-Erling Smørgrav /* 825af12a3e7SDag-Erling Smørgrav * User has requested strict host key checking. We 826af12a3e7SDag-Erling Smørgrav * will not add the host key automatically. The only 827af12a3e7SDag-Erling Smørgrav * alternative left is to abort. 828af12a3e7SDag-Erling Smørgrav */ 829af12a3e7SDag-Erling Smørgrav error("No %s host key is known for %.200s and you " 830af12a3e7SDag-Erling Smørgrav "have requested strict checking.", type, host); 831af12a3e7SDag-Erling Smørgrav goto fail; 832e8aafc91SKris Kennaway } else if (options.strict_host_key_checking == 2) { 833cf2b5f3bSDag-Erling Smørgrav char msg1[1024], msg2[1024]; 834cf2b5f3bSDag-Erling Smørgrav 835*4a421b63SDag-Erling Smørgrav if (show_other_keys(host_hostkeys, host_key)) 836cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), 837cf2b5f3bSDag-Erling Smørgrav "\nbut keys of different type are already" 838cf2b5f3bSDag-Erling Smørgrav " known for this host."); 839cf2b5f3bSDag-Erling Smørgrav else 840cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), "."); 841e8aafc91SKris Kennaway /* The default */ 842ca3176e7SBrian Feldman fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 843d4af9e69SDag-Erling Smørgrav ra = key_fingerprint(host_key, SSH_FP_MD5, 844d4af9e69SDag-Erling Smørgrav SSH_FP_RANDOMART); 845cf2b5f3bSDag-Erling Smørgrav msg2[0] = '\0'; 846cf2b5f3bSDag-Erling Smørgrav if (options.verify_host_key_dns) { 8471ec0d754SDag-Erling Smørgrav if (matching_host_key_dns) 848cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 849cf2b5f3bSDag-Erling Smørgrav "Matching host key fingerprint" 850cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 851cf2b5f3bSDag-Erling Smørgrav else 852cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 853cf2b5f3bSDag-Erling Smørgrav "No matching host key fingerprint" 854cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 855cf2b5f3bSDag-Erling Smørgrav } 856af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 857af12a3e7SDag-Erling Smørgrav "The authenticity of host '%.200s (%s)' can't be " 858f388f5efSDag-Erling Smørgrav "established%s\n" 859d4af9e69SDag-Erling Smørgrav "%s key fingerprint is %s.%s%s\n%s" 860af12a3e7SDag-Erling Smørgrav "Are you sure you want to continue connecting " 861f388f5efSDag-Erling Smørgrav "(yes/no)? ", 862d4af9e69SDag-Erling Smørgrav host, ip, msg1, type, fp, 863d4af9e69SDag-Erling Smørgrav options.visual_host_key ? "\n" : "", 864d4af9e69SDag-Erling Smørgrav options.visual_host_key ? ra : "", 865d4af9e69SDag-Erling Smørgrav msg2); 866d4af9e69SDag-Erling Smørgrav xfree(ra); 867ca3176e7SBrian Feldman xfree(fp); 868af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 869af12a3e7SDag-Erling Smørgrav goto fail; 870e8aafc91SKris Kennaway } 871af12a3e7SDag-Erling Smørgrav /* 872af12a3e7SDag-Erling Smørgrav * If not in strict mode, add the key automatically to the 873af12a3e7SDag-Erling Smørgrav * local known_hosts file. 874af12a3e7SDag-Erling Smørgrav */ 875aa49c926SDag-Erling Smørgrav if (options.check_host_ip && ip_status == HOST_NEW) { 876*4a421b63SDag-Erling Smørgrav snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 877aa49c926SDag-Erling Smørgrav hostp = hostline; 878aa49c926SDag-Erling Smørgrav if (options.hash_known_hosts) { 879aa49c926SDag-Erling Smørgrav /* Add hash of host and IP separately */ 880aa49c926SDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfile, host, 881aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts) && 882aa49c926SDag-Erling Smørgrav add_host_to_hostfile(user_hostfile, ip, 883aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts); 884aa49c926SDag-Erling Smørgrav } else { 885aa49c926SDag-Erling Smørgrav /* Add unhashed "host,ip" */ 886aa49c926SDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfile, 887aa49c926SDag-Erling Smørgrav hostline, host_key, 888aa49c926SDag-Erling Smørgrav options.hash_known_hosts); 889aa49c926SDag-Erling Smørgrav } 890aa49c926SDag-Erling Smørgrav } else { 891aa49c926SDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfile, host, host_key, 892aa49c926SDag-Erling Smørgrav options.hash_known_hosts); 893aa49c926SDag-Erling Smørgrav hostp = host; 894aa49c926SDag-Erling Smørgrav } 895aa49c926SDag-Erling Smørgrav 896aa49c926SDag-Erling Smørgrav if (!r) 897cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the host to the list of known " 898af12a3e7SDag-Erling Smørgrav "hosts (%.500s).", user_hostfile); 899e8aafc91SKris Kennaway else 900cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added '%.200s' (%s) to the " 901af12a3e7SDag-Erling Smørgrav "list of known hosts.", hostp, type); 902e8aafc91SKris Kennaway break; 903b15c8340SDag-Erling Smørgrav case HOST_REVOKED: 904b15c8340SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 905b15c8340SDag-Erling Smørgrav error("@ WARNING: REVOKED HOST KEY DETECTED! @"); 906b15c8340SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 907b15c8340SDag-Erling Smørgrav error("The %s host key for %s is marked as revoked.", type, host); 908b15c8340SDag-Erling Smørgrav error("This could mean that a stolen key is being used to"); 909b15c8340SDag-Erling Smørgrav error("impersonate this host."); 910b15c8340SDag-Erling Smørgrav 911b15c8340SDag-Erling Smørgrav /* 912b15c8340SDag-Erling Smørgrav * If strict host key checking is in use, the user will have 913b15c8340SDag-Erling Smørgrav * to edit the key manually and we can only abort. 914b15c8340SDag-Erling Smørgrav */ 915b15c8340SDag-Erling Smørgrav if (options.strict_host_key_checking) { 916b15c8340SDag-Erling Smørgrav error("%s host key for %.200s was revoked and you have " 917b15c8340SDag-Erling Smørgrav "requested strict checking.", type, host); 918b15c8340SDag-Erling Smørgrav goto fail; 919b15c8340SDag-Erling Smørgrav } 920b15c8340SDag-Erling Smørgrav goto continue_unsafe; 921b15c8340SDag-Erling Smørgrav 922e8aafc91SKris Kennaway case HOST_CHANGED: 923b15c8340SDag-Erling Smørgrav if (want_cert) { 924b15c8340SDag-Erling Smørgrav /* 925b15c8340SDag-Erling Smørgrav * This is only a debug() since it is valid to have 926b15c8340SDag-Erling Smørgrav * CAs with wildcard DNS matches that don't match 927b15c8340SDag-Erling Smørgrav * all hosts that one might visit. 928b15c8340SDag-Erling Smørgrav */ 929b15c8340SDag-Erling Smørgrav debug("Host certificate authority does not " 930*4a421b63SDag-Erling Smørgrav "match %s in %s:%lu", CA_MARKER, 931*4a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 932b15c8340SDag-Erling Smørgrav goto fail; 933b15c8340SDag-Erling Smørgrav } 934333ee039SDag-Erling Smørgrav if (readonly == ROQUIET) 935333ee039SDag-Erling Smørgrav goto fail; 936e8aafc91SKris Kennaway if (options.check_host_ip && host_ip_differ) { 93721e764dfSDag-Erling Smørgrav char *key_msg; 938e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 93921e764dfSDag-Erling Smørgrav key_msg = "is unknown"; 940e8aafc91SKris Kennaway else if (ip_status == HOST_OK) 94121e764dfSDag-Erling Smørgrav key_msg = "is unchanged"; 942e8aafc91SKris Kennaway else 94321e764dfSDag-Erling Smørgrav key_msg = "has a different value"; 944e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 945e8aafc91SKris Kennaway error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 946e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 947e8aafc91SKris Kennaway error("The %s host key for %s has changed,", type, host); 948d4af9e69SDag-Erling Smørgrav error("and the key for the corresponding IP address %s", ip); 94921e764dfSDag-Erling Smørgrav error("%s. This could either mean that", key_msg); 950e8aafc91SKris Kennaway error("DNS SPOOFING is happening or the IP address for the host"); 951ca3176e7SBrian Feldman error("and its host key have changed at the same time."); 952ca3176e7SBrian Feldman if (ip_status != HOST_NEW) 953*4a421b63SDag-Erling Smørgrav error("Offending key for IP in %s:%lu", 954*4a421b63SDag-Erling Smørgrav ip_found->file, ip_found->line); 955e8aafc91SKris Kennaway } 956e8aafc91SKris Kennaway /* The host key has changed. */ 9571ec0d754SDag-Erling Smørgrav warn_changed_key(host_key); 958e8aafc91SKris Kennaway error("Add correct host key in %.100s to get rid of this message.", 959e8aafc91SKris Kennaway user_hostfile); 960*4a421b63SDag-Erling Smørgrav error("Offending %s key in %s:%lu", key_type(host_found->key), 961*4a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 962e8aafc91SKris Kennaway 963e8aafc91SKris Kennaway /* 964e8aafc91SKris Kennaway * If strict host key checking is in use, the user will have 965e8aafc91SKris Kennaway * to edit the key manually and we can only abort. 966e8aafc91SKris Kennaway */ 967af12a3e7SDag-Erling Smørgrav if (options.strict_host_key_checking) { 968af12a3e7SDag-Erling Smørgrav error("%s host key for %.200s has changed and you have " 969af12a3e7SDag-Erling Smørgrav "requested strict checking.", type, host); 970af12a3e7SDag-Erling Smørgrav goto fail; 971af12a3e7SDag-Erling Smørgrav } 972e8aafc91SKris Kennaway 973b15c8340SDag-Erling Smørgrav continue_unsafe: 974e8aafc91SKris Kennaway /* 975e8aafc91SKris Kennaway * If strict host key checking has not been requested, allow 976cf2b5f3bSDag-Erling Smørgrav * the connection but without MITM-able authentication or 977333ee039SDag-Erling Smørgrav * forwarding. 978e8aafc91SKris Kennaway */ 979e8aafc91SKris Kennaway if (options.password_authentication) { 980af12a3e7SDag-Erling Smørgrav error("Password authentication is disabled to avoid " 981af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 982e8aafc91SKris Kennaway options.password_authentication = 0; 983d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 984e8aafc91SKris Kennaway } 985cf2b5f3bSDag-Erling Smørgrav if (options.kbd_interactive_authentication) { 986cf2b5f3bSDag-Erling Smørgrav error("Keyboard-interactive authentication is disabled" 987cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 988cf2b5f3bSDag-Erling Smørgrav options.kbd_interactive_authentication = 0; 989cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 990d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 991cf2b5f3bSDag-Erling Smørgrav } 992cf2b5f3bSDag-Erling Smørgrav if (options.challenge_response_authentication) { 993cf2b5f3bSDag-Erling Smørgrav error("Challenge/response authentication is disabled" 994cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 995cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 996d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 997cf2b5f3bSDag-Erling Smørgrav } 998e8aafc91SKris Kennaway if (options.forward_agent) { 999af12a3e7SDag-Erling Smørgrav error("Agent forwarding is disabled to avoid " 1000af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1001e8aafc91SKris Kennaway options.forward_agent = 0; 1002d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1003e8aafc91SKris Kennaway } 1004ca3176e7SBrian Feldman if (options.forward_x11) { 1005af12a3e7SDag-Erling Smørgrav error("X11 forwarding is disabled to avoid " 1006af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1007ca3176e7SBrian Feldman options.forward_x11 = 0; 1008d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1009ca3176e7SBrian Feldman } 1010af12a3e7SDag-Erling Smørgrav if (options.num_local_forwards > 0 || 1011af12a3e7SDag-Erling Smørgrav options.num_remote_forwards > 0) { 1012af12a3e7SDag-Erling Smørgrav error("Port forwarding is disabled to avoid " 1013af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1014af12a3e7SDag-Erling Smørgrav options.num_local_forwards = 1015af12a3e7SDag-Erling Smørgrav options.num_remote_forwards = 0; 1016d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1017ca3176e7SBrian Feldman } 1018333ee039SDag-Erling Smørgrav if (options.tun_open != SSH_TUNMODE_NO) { 1019333ee039SDag-Erling Smørgrav error("Tunnel forwarding is disabled to avoid " 1020333ee039SDag-Erling Smørgrav "man-in-the-middle attacks."); 1021333ee039SDag-Erling Smørgrav options.tun_open = SSH_TUNMODE_NO; 1022d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1023333ee039SDag-Erling Smørgrav } 1024d4af9e69SDag-Erling Smørgrav if (options.exit_on_forward_failure && cancelled_forwarding) 1025d4af9e69SDag-Erling Smørgrav fatal("Error: forwarding disabled due to host key " 1026d4af9e69SDag-Erling Smørgrav "check failure"); 1027d4af9e69SDag-Erling Smørgrav 1028e8aafc91SKris Kennaway /* 1029e8aafc91SKris Kennaway * XXX Should permit the user to change to use the new id. 1030e8aafc91SKris Kennaway * This could be done by converting the host key to an 1031e8aafc91SKris Kennaway * identifying sentence, tell that the host identifies itself 1032b15c8340SDag-Erling Smørgrav * by that sentence, and ask the user if he/she wishes to 1033e8aafc91SKris Kennaway * accept the authentication. 1034e8aafc91SKris Kennaway */ 1035e8aafc91SKris Kennaway break; 1036f388f5efSDag-Erling Smørgrav case HOST_FOUND: 1037f388f5efSDag-Erling Smørgrav fatal("internal error"); 1038f388f5efSDag-Erling Smørgrav break; 1039e8aafc91SKris Kennaway } 1040ca3176e7SBrian Feldman 1041ca3176e7SBrian Feldman if (options.check_host_ip && host_status != HOST_CHANGED && 1042ca3176e7SBrian Feldman ip_status == HOST_CHANGED) { 1043af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 1044af12a3e7SDag-Erling Smørgrav "Warning: the %s host key for '%.200s' " 1045af12a3e7SDag-Erling Smørgrav "differs from the key for the IP address '%.128s'" 1046*4a421b63SDag-Erling Smørgrav "\nOffending key for IP in %s:%lu", 1047*4a421b63SDag-Erling Smørgrav type, host, ip, ip_found->file, ip_found->line); 1048af12a3e7SDag-Erling Smørgrav if (host_status == HOST_OK) { 1049af12a3e7SDag-Erling Smørgrav len = strlen(msg); 1050af12a3e7SDag-Erling Smørgrav snprintf(msg + len, sizeof(msg) - len, 1051*4a421b63SDag-Erling Smørgrav "\nMatching host key in %s:%lu", 1052*4a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1053af12a3e7SDag-Erling Smørgrav } 1054ca3176e7SBrian Feldman if (options.strict_host_key_checking == 1) { 1055cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 1056af12a3e7SDag-Erling Smørgrav error("Exiting, you have requested strict checking."); 1057af12a3e7SDag-Erling Smørgrav goto fail; 1058ca3176e7SBrian Feldman } else if (options.strict_host_key_checking == 2) { 1059af12a3e7SDag-Erling Smørgrav strlcat(msg, "\nAre you sure you want " 1060af12a3e7SDag-Erling Smørgrav "to continue connecting (yes/no)? ", sizeof(msg)); 1061af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 1062af12a3e7SDag-Erling Smørgrav goto fail; 1063af12a3e7SDag-Erling Smørgrav } else { 1064cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 1065ca3176e7SBrian Feldman } 1066ca3176e7SBrian Feldman } 1067ca3176e7SBrian Feldman 1068e8aafc91SKris Kennaway xfree(ip); 1069333ee039SDag-Erling Smørgrav xfree(host); 1070*4a421b63SDag-Erling Smørgrav if (host_hostkeys != NULL) 1071*4a421b63SDag-Erling Smørgrav free_hostkeys(host_hostkeys); 1072*4a421b63SDag-Erling Smørgrav if (ip_hostkeys != NULL) 1073*4a421b63SDag-Erling Smørgrav free_hostkeys(ip_hostkeys); 1074af12a3e7SDag-Erling Smørgrav return 0; 1075af12a3e7SDag-Erling Smørgrav 1076af12a3e7SDag-Erling Smørgrav fail: 1077b15c8340SDag-Erling Smørgrav if (want_cert && host_status != HOST_REVOKED) { 1078b15c8340SDag-Erling Smørgrav /* 1079b15c8340SDag-Erling Smørgrav * No matching certificate. Downgrade cert to raw key and 1080b15c8340SDag-Erling Smørgrav * search normally. 1081b15c8340SDag-Erling Smørgrav */ 1082b15c8340SDag-Erling Smørgrav debug("No matching CA found. Retry with plain key"); 1083b15c8340SDag-Erling Smørgrav raw_key = key_from_private(host_key); 1084b15c8340SDag-Erling Smørgrav if (key_drop_cert(raw_key) != 0) 1085b15c8340SDag-Erling Smørgrav fatal("Couldn't drop certificate"); 1086b15c8340SDag-Erling Smørgrav host_key = raw_key; 1087b15c8340SDag-Erling Smørgrav goto retry; 1088b15c8340SDag-Erling Smørgrav } 1089b15c8340SDag-Erling Smørgrav if (raw_key != NULL) 1090b15c8340SDag-Erling Smørgrav key_free(raw_key); 1091af12a3e7SDag-Erling Smørgrav xfree(ip); 1092333ee039SDag-Erling Smørgrav xfree(host); 1093*4a421b63SDag-Erling Smørgrav if (host_hostkeys != NULL) 1094*4a421b63SDag-Erling Smørgrav free_hostkeys(host_hostkeys); 1095*4a421b63SDag-Erling Smørgrav if (ip_hostkeys != NULL) 1096*4a421b63SDag-Erling Smørgrav free_hostkeys(ip_hostkeys); 1097af12a3e7SDag-Erling Smørgrav return -1; 1098e8aafc91SKris Kennaway } 1099511b41d2SMark Murray 1100cf2b5f3bSDag-Erling Smørgrav /* returns 0 if key verifies or -1 if key does NOT verify */ 1101fe5fd017SMark Murray int 1102af12a3e7SDag-Erling Smørgrav verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 1103fe5fd017SMark Murray { 1104af12a3e7SDag-Erling Smørgrav struct stat st; 11051ec0d754SDag-Erling Smørgrav int flags = 0; 1106*4a421b63SDag-Erling Smørgrav char *fp; 1107*4a421b63SDag-Erling Smørgrav 1108*4a421b63SDag-Erling Smørgrav fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 1109*4a421b63SDag-Erling Smørgrav debug("Server host key: %s %s", key_type(host_key), fp); 1110*4a421b63SDag-Erling Smørgrav xfree(fp); 1111fe5fd017SMark Murray 1112b15c8340SDag-Erling Smørgrav /* XXX certs are not yet supported for DNS */ 1113b15c8340SDag-Erling Smørgrav if (!key_is_cert(host_key) && options.verify_host_key_dns && 11141ec0d754SDag-Erling Smørgrav verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { 11151ec0d754SDag-Erling Smørgrav 11161ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_FOUND) { 11171ec0d754SDag-Erling Smørgrav 11181ec0d754SDag-Erling Smørgrav if (options.verify_host_key_dns == 1 && 11191ec0d754SDag-Erling Smørgrav flags & DNS_VERIFY_MATCH && 11201ec0d754SDag-Erling Smørgrav flags & DNS_VERIFY_SECURE) 1121cf2b5f3bSDag-Erling Smørgrav return 0; 11221ec0d754SDag-Erling Smørgrav 11231ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_MATCH) { 11241ec0d754SDag-Erling Smørgrav matching_host_key_dns = 1; 11251ec0d754SDag-Erling Smørgrav } else { 11261ec0d754SDag-Erling Smørgrav warn_changed_key(host_key); 11271ec0d754SDag-Erling Smørgrav error("Update the SSHFP RR in DNS with the new " 11281ec0d754SDag-Erling Smørgrav "host key to get rid of this message."); 1129cf2b5f3bSDag-Erling Smørgrav } 1130cf2b5f3bSDag-Erling Smørgrav } 11311ec0d754SDag-Erling Smørgrav } 1132cf2b5f3bSDag-Erling Smørgrav 1133af12a3e7SDag-Erling Smørgrav /* return ok if the key can be found in an old keyfile */ 1134af12a3e7SDag-Erling Smørgrav if (stat(options.system_hostfile2, &st) == 0 || 1135af12a3e7SDag-Erling Smørgrav stat(options.user_hostfile2, &st) == 0) { 1136333ee039SDag-Erling Smørgrav if (check_host_key(host, hostaddr, options.port, host_key, 1137333ee039SDag-Erling Smørgrav RDONLY, options.user_hostfile2, 1138333ee039SDag-Erling Smørgrav options.system_hostfile2) == 0) 1139af12a3e7SDag-Erling Smørgrav return 0; 1140fe5fd017SMark Murray } 1141333ee039SDag-Erling Smørgrav return check_host_key(host, hostaddr, options.port, host_key, 1142333ee039SDag-Erling Smørgrav RDRW, options.user_hostfile, options.system_hostfile); 1143fe5fd017SMark Murray } 1144fe5fd017SMark Murray 1145511b41d2SMark Murray /* 1146511b41d2SMark Murray * Starts a dialog with the server, and authenticates the current user on the 1147511b41d2SMark Murray * server. This does not need any extra privileges. The basic connection 1148511b41d2SMark Murray * to the server must already have been established before this is called. 1149511b41d2SMark Murray * If login fails, this function prints an error and never returns. 1150511b41d2SMark Murray * This function does not require super-user privileges. 1151511b41d2SMark Murray */ 1152511b41d2SMark Murray void 115380628bacSDag-Erling Smørgrav ssh_login(Sensitive *sensitive, const char *orighost, 1154*4a421b63SDag-Erling Smørgrav struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) 1155511b41d2SMark Murray { 1156511b41d2SMark Murray char *host, *cp; 1157e8aafc91SKris Kennaway char *server_user, *local_user; 1158e8aafc91SKris Kennaway 1159e8aafc91SKris Kennaway local_user = xstrdup(pw->pw_name); 1160e8aafc91SKris Kennaway server_user = options.user ? options.user : local_user; 1161511b41d2SMark Murray 1162511b41d2SMark Murray /* Convert the user-supplied hostname into all lowercase. */ 1163511b41d2SMark Murray host = xstrdup(orighost); 1164511b41d2SMark Murray for (cp = host; *cp; cp++) 1165511b41d2SMark Murray if (isupper(*cp)) 1166333ee039SDag-Erling Smørgrav *cp = (char)tolower(*cp); 1167511b41d2SMark Murray 1168511b41d2SMark Murray /* Exchange protocol version identification strings with the server. */ 1169d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(timeout_ms); 1170511b41d2SMark Murray 1171511b41d2SMark Murray /* Put the connection into non-blocking mode. */ 1172511b41d2SMark Murray packet_set_nonblocking(); 1173511b41d2SMark Murray 1174511b41d2SMark Murray /* key exchange */ 1175511b41d2SMark Murray /* authenticate user */ 1176e8aafc91SKris Kennaway if (compat20) { 1177*4a421b63SDag-Erling Smørgrav ssh_kex2(host, hostaddr, port); 117880628bacSDag-Erling Smørgrav ssh_userauth2(local_user, server_user, host, sensitive); 1179e8aafc91SKris Kennaway } else { 1180e8aafc91SKris Kennaway ssh_kex(host, hostaddr); 118180628bacSDag-Erling Smørgrav ssh_userauth1(local_user, server_user, host, sensitive); 1182e8aafc91SKris Kennaway } 1183333ee039SDag-Erling Smørgrav xfree(local_user); 1184511b41d2SMark Murray } 1185e0fbb1d2SBrian Feldman 1186e0fbb1d2SBrian Feldman void 1187e0fbb1d2SBrian Feldman ssh_put_password(char *password) 1188e0fbb1d2SBrian Feldman { 1189e0fbb1d2SBrian Feldman int size; 1190e0fbb1d2SBrian Feldman char *padded; 1191e0fbb1d2SBrian Feldman 1192ca3176e7SBrian Feldman if (datafellows & SSH_BUG_PASSWORDPAD) { 1193af12a3e7SDag-Erling Smørgrav packet_put_cstring(password); 1194ca3176e7SBrian Feldman return; 1195ca3176e7SBrian Feldman } 1196e0fbb1d2SBrian Feldman size = roundup(strlen(password) + 1, 32); 1197333ee039SDag-Erling Smørgrav padded = xcalloc(1, size); 1198e0fbb1d2SBrian Feldman strlcpy(padded, password, size); 1199e0fbb1d2SBrian Feldman packet_put_string(padded, size); 1200e0fbb1d2SBrian Feldman memset(padded, 0, size); 1201e0fbb1d2SBrian Feldman xfree(padded); 1202e0fbb1d2SBrian Feldman } 1203f388f5efSDag-Erling Smørgrav 1204f388f5efSDag-Erling Smørgrav /* print all known host keys for a given host, but skip keys of given type */ 1205f388f5efSDag-Erling Smørgrav static int 1206*4a421b63SDag-Erling Smørgrav show_other_keys(struct hostkeys *hostkeys, Key *key) 1207f388f5efSDag-Erling Smørgrav { 1208*4a421b63SDag-Erling Smørgrav int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1}; 1209*4a421b63SDag-Erling Smørgrav int i, ret = 0; 1210*4a421b63SDag-Erling Smørgrav char *fp, *ra; 1211*4a421b63SDag-Erling Smørgrav const struct hostkey_entry *found; 1212f388f5efSDag-Erling Smørgrav 1213f388f5efSDag-Erling Smørgrav for (i = 0; type[i] != -1; i++) { 1214f388f5efSDag-Erling Smørgrav if (type[i] == key->type) 1215f388f5efSDag-Erling Smørgrav continue; 1216*4a421b63SDag-Erling Smørgrav if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) 1217f388f5efSDag-Erling Smørgrav continue; 1218*4a421b63SDag-Erling Smørgrav fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX); 1219*4a421b63SDag-Erling Smørgrav ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART); 1220*4a421b63SDag-Erling Smørgrav logit("WARNING: %s key found for host %s\n" 1221*4a421b63SDag-Erling Smørgrav "in %s:%lu\n" 1222*4a421b63SDag-Erling Smørgrav "%s key fingerprint %s.", 1223*4a421b63SDag-Erling Smørgrav key_type(found->key), 1224*4a421b63SDag-Erling Smørgrav found->host, found->file, found->line, 1225*4a421b63SDag-Erling Smørgrav key_type(found->key), fp); 1226*4a421b63SDag-Erling Smørgrav if (options.visual_host_key) 1227*4a421b63SDag-Erling Smørgrav logit("%s", ra); 1228*4a421b63SDag-Erling Smørgrav xfree(ra); 1229*4a421b63SDag-Erling Smørgrav xfree(fp); 1230*4a421b63SDag-Erling Smørgrav ret = 1; 1231f388f5efSDag-Erling Smørgrav } 1232*4a421b63SDag-Erling Smørgrav return ret; 1233f388f5efSDag-Erling Smørgrav } 12341ec0d754SDag-Erling Smørgrav 12351ec0d754SDag-Erling Smørgrav static void 12361ec0d754SDag-Erling Smørgrav warn_changed_key(Key *host_key) 12371ec0d754SDag-Erling Smørgrav { 12381ec0d754SDag-Erling Smørgrav char *fp; 12391ec0d754SDag-Erling Smørgrav 12401ec0d754SDag-Erling Smørgrav fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 12411ec0d754SDag-Erling Smørgrav 12421ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 12431ec0d754SDag-Erling Smørgrav error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 12441ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 12451ec0d754SDag-Erling Smørgrav error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 12461ec0d754SDag-Erling Smørgrav error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 1247*4a421b63SDag-Erling Smørgrav error("It is also possible that a host key has just been changed."); 12481ec0d754SDag-Erling Smørgrav error("The fingerprint for the %s key sent by the remote host is\n%s.", 1249*4a421b63SDag-Erling Smørgrav key_type(host_key), fp); 12501ec0d754SDag-Erling Smørgrav error("Please contact your system administrator."); 12511ec0d754SDag-Erling Smørgrav 12521ec0d754SDag-Erling Smørgrav xfree(fp); 12531ec0d754SDag-Erling Smørgrav } 1254b74df5b2SDag-Erling Smørgrav 1255b74df5b2SDag-Erling Smørgrav /* 1256b74df5b2SDag-Erling Smørgrav * Execute a local command 1257b74df5b2SDag-Erling Smørgrav */ 1258b74df5b2SDag-Erling Smørgrav int 1259b74df5b2SDag-Erling Smørgrav ssh_local_cmd(const char *args) 1260b74df5b2SDag-Erling Smørgrav { 1261b74df5b2SDag-Erling Smørgrav char *shell; 1262b74df5b2SDag-Erling Smørgrav pid_t pid; 1263b74df5b2SDag-Erling Smørgrav int status; 1264*4a421b63SDag-Erling Smørgrav void (*osighand)(int); 1265b74df5b2SDag-Erling Smørgrav 1266b74df5b2SDag-Erling Smørgrav if (!options.permit_local_command || 1267b74df5b2SDag-Erling Smørgrav args == NULL || !*args) 1268b74df5b2SDag-Erling Smørgrav return (1); 1269b74df5b2SDag-Erling Smørgrav 1270*4a421b63SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 1271b74df5b2SDag-Erling Smørgrav shell = _PATH_BSHELL; 1272b74df5b2SDag-Erling Smørgrav 1273*4a421b63SDag-Erling Smørgrav osighand = signal(SIGCHLD, SIG_DFL); 1274b74df5b2SDag-Erling Smørgrav pid = fork(); 1275b74df5b2SDag-Erling Smørgrav if (pid == 0) { 1276*4a421b63SDag-Erling Smørgrav signal(SIGPIPE, SIG_DFL); 1277b74df5b2SDag-Erling Smørgrav debug3("Executing %s -c \"%s\"", shell, args); 1278b74df5b2SDag-Erling Smørgrav execl(shell, shell, "-c", args, (char *)NULL); 1279b74df5b2SDag-Erling Smørgrav error("Couldn't execute %s -c \"%s\": %s", 1280b74df5b2SDag-Erling Smørgrav shell, args, strerror(errno)); 1281b74df5b2SDag-Erling Smørgrav _exit(1); 1282b74df5b2SDag-Erling Smørgrav } else if (pid == -1) 1283b74df5b2SDag-Erling Smørgrav fatal("fork failed: %.100s", strerror(errno)); 1284b74df5b2SDag-Erling Smørgrav while (waitpid(pid, &status, 0) == -1) 1285b74df5b2SDag-Erling Smørgrav if (errno != EINTR) 1286b74df5b2SDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 1287*4a421b63SDag-Erling Smørgrav signal(SIGCHLD, osighand); 1288b74df5b2SDag-Erling Smørgrav 1289b74df5b2SDag-Erling Smørgrav if (!WIFEXITED(status)) 1290b74df5b2SDag-Erling Smørgrav return (1); 1291b74df5b2SDag-Erling Smørgrav 1292b74df5b2SDag-Erling Smørgrav return (WEXITSTATUS(status)); 1293b74df5b2SDag-Erling Smørgrav } 1294