1*ce74bacaSMatthew Dillon /* $OpenBSD: sshconnect.c,v 1.287 2017/09/14 04:32:21 djm Exp $ */ 218de8d7fSPeter Avalos /* 318de8d7fSPeter Avalos * Author: Tatu Ylonen <ylo@cs.hut.fi> 418de8d7fSPeter Avalos * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 518de8d7fSPeter Avalos * All rights reserved 618de8d7fSPeter Avalos * Code to connect to a remote host, and to perform the client side of the 718de8d7fSPeter Avalos * login (authentication) dialog. 818de8d7fSPeter Avalos * 918de8d7fSPeter Avalos * As far as I am concerned, the code I have written for this software 1018de8d7fSPeter Avalos * can be used freely for any purpose. Any derived versions of this 1118de8d7fSPeter Avalos * software must be clearly marked as such, and if the derived work is 1218de8d7fSPeter Avalos * incompatible with the protocol description in the RFC file, it must be 1318de8d7fSPeter Avalos * called by a name other than "ssh" or "Secure Shell". 1418de8d7fSPeter Avalos */ 1518de8d7fSPeter Avalos 1618de8d7fSPeter Avalos #include "includes.h" 1718de8d7fSPeter Avalos 1818de8d7fSPeter Avalos #include <sys/types.h> 1918de8d7fSPeter Avalos #include <sys/wait.h> 2018de8d7fSPeter Avalos #include <sys/stat.h> 2118de8d7fSPeter Avalos #include <sys/socket.h> 2218de8d7fSPeter Avalos #ifdef HAVE_SYS_TIME_H 2318de8d7fSPeter Avalos # include <sys/time.h> 2418de8d7fSPeter Avalos #endif 2518de8d7fSPeter Avalos 2618de8d7fSPeter Avalos #include <netinet/in.h> 2718de8d7fSPeter Avalos #include <arpa/inet.h> 2818de8d7fSPeter Avalos 2918de8d7fSPeter Avalos #include <ctype.h> 3018de8d7fSPeter Avalos #include <errno.h> 31856ea928SPeter Avalos #include <fcntl.h> 3218de8d7fSPeter Avalos #include <netdb.h> 3318de8d7fSPeter Avalos #ifdef HAVE_PATHS_H 3418de8d7fSPeter Avalos #include <paths.h> 3518de8d7fSPeter Avalos #endif 3618de8d7fSPeter Avalos #include <pwd.h> 37*ce74bacaSMatthew Dillon #ifdef HAVE_POLL_H 38*ce74bacaSMatthew Dillon #include <poll.h> 39*ce74bacaSMatthew Dillon #endif 409f304aafSPeter Avalos #include <signal.h> 4118de8d7fSPeter Avalos #include <stdarg.h> 4218de8d7fSPeter Avalos #include <stdio.h> 4318de8d7fSPeter Avalos #include <stdlib.h> 4418de8d7fSPeter Avalos #include <string.h> 4518de8d7fSPeter Avalos #include <unistd.h> 4618de8d7fSPeter Avalos 4718de8d7fSPeter Avalos #include "xmalloc.h" 4818de8d7fSPeter Avalos #include "key.h" 4918de8d7fSPeter Avalos #include "hostfile.h" 5018de8d7fSPeter Avalos #include "ssh.h" 5118de8d7fSPeter Avalos #include "buffer.h" 5218de8d7fSPeter Avalos #include "packet.h" 5318de8d7fSPeter Avalos #include "uidswap.h" 5418de8d7fSPeter Avalos #include "compat.h" 5518de8d7fSPeter Avalos #include "key.h" 5618de8d7fSPeter Avalos #include "sshconnect.h" 5718de8d7fSPeter Avalos #include "hostfile.h" 5818de8d7fSPeter Avalos #include "log.h" 5936e94dc5SPeter Avalos #include "misc.h" 6018de8d7fSPeter Avalos #include "readconf.h" 6118de8d7fSPeter Avalos #include "atomicio.h" 6218de8d7fSPeter Avalos #include "dns.h" 6336e94dc5SPeter Avalos #include "monitor_fdpass.h" 64856ea928SPeter Avalos #include "ssh2.h" 6518de8d7fSPeter Avalos #include "version.h" 66e9778795SPeter Avalos #include "authfile.h" 67e9778795SPeter Avalos #include "ssherr.h" 68e9778795SPeter Avalos #include "authfd.h" 6918de8d7fSPeter Avalos 7018de8d7fSPeter Avalos char *client_version_string = NULL; 7118de8d7fSPeter Avalos char *server_version_string = NULL; 72*ce74bacaSMatthew Dillon struct sshkey *previous_host_key = NULL; 7318de8d7fSPeter Avalos 7418de8d7fSPeter Avalos static int matching_host_key_dns = 0; 7518de8d7fSPeter Avalos 769f304aafSPeter Avalos static pid_t proxy_command_pid = 0; 779f304aafSPeter Avalos 7818de8d7fSPeter Avalos /* import */ 7918de8d7fSPeter Avalos extern Options options; 8018de8d7fSPeter Avalos extern char *__progname; 8118de8d7fSPeter Avalos extern uid_t original_real_uid; 8218de8d7fSPeter Avalos extern uid_t original_effective_uid; 8318de8d7fSPeter Avalos 84*ce74bacaSMatthew Dillon static int show_other_keys(struct hostkeys *, struct sshkey *); 85*ce74bacaSMatthew Dillon static void warn_changed_key(struct sshkey *); 8618de8d7fSPeter Avalos 8736e94dc5SPeter Avalos /* Expand a proxy command */ 8836e94dc5SPeter Avalos static char * 8936e94dc5SPeter Avalos expand_proxy_command(const char *proxy_command, const char *user, 9036e94dc5SPeter Avalos const char *host, int port) 9136e94dc5SPeter Avalos { 9236e94dc5SPeter Avalos char *tmp, *ret, strport[NI_MAXSERV]; 9336e94dc5SPeter Avalos 9436e94dc5SPeter Avalos snprintf(strport, sizeof strport, "%d", port); 9536e94dc5SPeter Avalos xasprintf(&tmp, "exec %s", proxy_command); 9636e94dc5SPeter Avalos ret = percent_expand(tmp, "h", host, "p", strport, 9736e94dc5SPeter Avalos "r", options.user, (char *)NULL); 9836e94dc5SPeter Avalos free(tmp); 9936e94dc5SPeter Avalos return ret; 10036e94dc5SPeter Avalos } 10136e94dc5SPeter Avalos 10236e94dc5SPeter Avalos /* 10336e94dc5SPeter Avalos * Connect to the given ssh server using a proxy command that passes a 10436e94dc5SPeter Avalos * a connected fd back to us. 10536e94dc5SPeter Avalos */ 10636e94dc5SPeter Avalos static int 107*ce74bacaSMatthew Dillon ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host, u_short port, 10836e94dc5SPeter Avalos const char *proxy_command) 10936e94dc5SPeter Avalos { 11036e94dc5SPeter Avalos char *command_string; 11136e94dc5SPeter Avalos int sp[2], sock; 11236e94dc5SPeter Avalos pid_t pid; 11336e94dc5SPeter Avalos char *shell; 11436e94dc5SPeter Avalos 11536e94dc5SPeter Avalos if ((shell = getenv("SHELL")) == NULL) 11636e94dc5SPeter Avalos shell = _PATH_BSHELL; 11736e94dc5SPeter Avalos 11836e94dc5SPeter Avalos if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) 11936e94dc5SPeter Avalos fatal("Could not create socketpair to communicate with " 12036e94dc5SPeter Avalos "proxy dialer: %.100s", strerror(errno)); 12136e94dc5SPeter Avalos 12236e94dc5SPeter Avalos command_string = expand_proxy_command(proxy_command, options.user, 12336e94dc5SPeter Avalos host, port); 12436e94dc5SPeter Avalos debug("Executing proxy dialer command: %.500s", command_string); 12536e94dc5SPeter Avalos 12636e94dc5SPeter Avalos /* Fork and execute the proxy command. */ 12736e94dc5SPeter Avalos if ((pid = fork()) == 0) { 12836e94dc5SPeter Avalos char *argv[10]; 12936e94dc5SPeter Avalos 13036e94dc5SPeter Avalos /* Child. Permanently give up superuser privileges. */ 13136e94dc5SPeter Avalos permanently_drop_suid(original_real_uid); 13236e94dc5SPeter Avalos 13336e94dc5SPeter Avalos close(sp[1]); 13436e94dc5SPeter Avalos /* Redirect stdin and stdout. */ 13536e94dc5SPeter Avalos if (sp[0] != 0) { 13636e94dc5SPeter Avalos if (dup2(sp[0], 0) < 0) 13736e94dc5SPeter Avalos perror("dup2 stdin"); 13836e94dc5SPeter Avalos } 13936e94dc5SPeter Avalos if (sp[0] != 1) { 14036e94dc5SPeter Avalos if (dup2(sp[0], 1) < 0) 14136e94dc5SPeter Avalos perror("dup2 stdout"); 14236e94dc5SPeter Avalos } 14336e94dc5SPeter Avalos if (sp[0] >= 2) 14436e94dc5SPeter Avalos close(sp[0]); 14536e94dc5SPeter Avalos 14636e94dc5SPeter Avalos /* 14736e94dc5SPeter Avalos * Stderr is left as it is so that error messages get 14836e94dc5SPeter Avalos * printed on the user's terminal. 14936e94dc5SPeter Avalos */ 15036e94dc5SPeter Avalos argv[0] = shell; 15136e94dc5SPeter Avalos argv[1] = "-c"; 15236e94dc5SPeter Avalos argv[2] = command_string; 15336e94dc5SPeter Avalos argv[3] = NULL; 15436e94dc5SPeter Avalos 15536e94dc5SPeter Avalos /* 15636e94dc5SPeter Avalos * Execute the proxy command. 15736e94dc5SPeter Avalos * Note that we gave up any extra privileges above. 15836e94dc5SPeter Avalos */ 15936e94dc5SPeter Avalos execv(argv[0], argv); 16036e94dc5SPeter Avalos perror(argv[0]); 16136e94dc5SPeter Avalos exit(1); 16236e94dc5SPeter Avalos } 16336e94dc5SPeter Avalos /* Parent. */ 16436e94dc5SPeter Avalos if (pid < 0) 16536e94dc5SPeter Avalos fatal("fork failed: %.100s", strerror(errno)); 16636e94dc5SPeter Avalos close(sp[0]); 16736e94dc5SPeter Avalos free(command_string); 16836e94dc5SPeter Avalos 16936e94dc5SPeter Avalos if ((sock = mm_receive_fd(sp[1])) == -1) 17036e94dc5SPeter Avalos fatal("proxy dialer did not pass back a connection"); 171e9778795SPeter Avalos close(sp[1]); 17236e94dc5SPeter Avalos 17336e94dc5SPeter Avalos while (waitpid(pid, NULL, 0) == -1) 17436e94dc5SPeter Avalos if (errno != EINTR) 17536e94dc5SPeter Avalos fatal("Couldn't wait for child: %s", strerror(errno)); 17636e94dc5SPeter Avalos 17736e94dc5SPeter Avalos /* Set the connection file descriptors. */ 178*ce74bacaSMatthew Dillon if (ssh_packet_set_connection(ssh, sock, sock) == NULL) 179*ce74bacaSMatthew Dillon return -1; /* ssh_packet_set_connection logs error */ 18036e94dc5SPeter Avalos 18136e94dc5SPeter Avalos return 0; 18236e94dc5SPeter Avalos } 18336e94dc5SPeter Avalos 18418de8d7fSPeter Avalos /* 18518de8d7fSPeter Avalos * Connect to the given ssh server using a proxy command. 18618de8d7fSPeter Avalos */ 18718de8d7fSPeter Avalos static int 188*ce74bacaSMatthew Dillon ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port, 189*ce74bacaSMatthew Dillon const char *proxy_command) 19018de8d7fSPeter Avalos { 19136e94dc5SPeter Avalos char *command_string; 19218de8d7fSPeter Avalos int pin[2], pout[2]; 19318de8d7fSPeter Avalos pid_t pid; 19436e94dc5SPeter Avalos char *shell; 19518de8d7fSPeter Avalos 1969f304aafSPeter Avalos if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 19718de8d7fSPeter Avalos shell = _PATH_BSHELL; 19818de8d7fSPeter Avalos 19918de8d7fSPeter Avalos /* Create pipes for communicating with the proxy. */ 20018de8d7fSPeter Avalos if (pipe(pin) < 0 || pipe(pout) < 0) 20118de8d7fSPeter Avalos fatal("Could not create pipes to communicate with the proxy: %.100s", 20218de8d7fSPeter Avalos strerror(errno)); 20318de8d7fSPeter Avalos 20436e94dc5SPeter Avalos command_string = expand_proxy_command(proxy_command, options.user, 20536e94dc5SPeter Avalos host, port); 20618de8d7fSPeter Avalos debug("Executing proxy command: %.500s", command_string); 20718de8d7fSPeter Avalos 20818de8d7fSPeter Avalos /* Fork and execute the proxy command. */ 20918de8d7fSPeter Avalos if ((pid = fork()) == 0) { 21018de8d7fSPeter Avalos char *argv[10]; 21118de8d7fSPeter Avalos 21218de8d7fSPeter Avalos /* Child. Permanently give up superuser privileges. */ 21318de8d7fSPeter Avalos permanently_drop_suid(original_real_uid); 21418de8d7fSPeter Avalos 21518de8d7fSPeter Avalos /* Redirect stdin and stdout. */ 21618de8d7fSPeter Avalos close(pin[1]); 21718de8d7fSPeter Avalos if (pin[0] != 0) { 21818de8d7fSPeter Avalos if (dup2(pin[0], 0) < 0) 21918de8d7fSPeter Avalos perror("dup2 stdin"); 22018de8d7fSPeter Avalos close(pin[0]); 22118de8d7fSPeter Avalos } 22218de8d7fSPeter Avalos close(pout[0]); 22318de8d7fSPeter Avalos if (dup2(pout[1], 1) < 0) 22418de8d7fSPeter Avalos perror("dup2 stdout"); 22518de8d7fSPeter Avalos /* Cannot be 1 because pin allocated two descriptors. */ 22618de8d7fSPeter Avalos close(pout[1]); 22718de8d7fSPeter Avalos 22818de8d7fSPeter Avalos /* Stderr is left as it is so that error messages get 22918de8d7fSPeter Avalos printed on the user's terminal. */ 23018de8d7fSPeter Avalos argv[0] = shell; 23118de8d7fSPeter Avalos argv[1] = "-c"; 23218de8d7fSPeter Avalos argv[2] = command_string; 23318de8d7fSPeter Avalos argv[3] = NULL; 23418de8d7fSPeter Avalos 23518de8d7fSPeter Avalos /* Execute the proxy command. Note that we gave up any 23618de8d7fSPeter Avalos extra privileges above. */ 2379f304aafSPeter Avalos signal(SIGPIPE, SIG_DFL); 23818de8d7fSPeter Avalos execv(argv[0], argv); 23918de8d7fSPeter Avalos perror(argv[0]); 24018de8d7fSPeter Avalos exit(1); 24118de8d7fSPeter Avalos } 24218de8d7fSPeter Avalos /* Parent. */ 24318de8d7fSPeter Avalos if (pid < 0) 24418de8d7fSPeter Avalos fatal("fork failed: %.100s", strerror(errno)); 24518de8d7fSPeter Avalos else 24618de8d7fSPeter Avalos proxy_command_pid = pid; /* save pid to clean up later */ 24718de8d7fSPeter Avalos 24818de8d7fSPeter Avalos /* Close child side of the descriptors. */ 24918de8d7fSPeter Avalos close(pin[0]); 25018de8d7fSPeter Avalos close(pout[1]); 25118de8d7fSPeter Avalos 25218de8d7fSPeter Avalos /* Free the command name. */ 25336e94dc5SPeter Avalos free(command_string); 25418de8d7fSPeter Avalos 25518de8d7fSPeter Avalos /* Set the connection file descriptors. */ 256*ce74bacaSMatthew Dillon if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL) 257*ce74bacaSMatthew Dillon return -1; /* ssh_packet_set_connection logs error */ 25818de8d7fSPeter Avalos 25918de8d7fSPeter Avalos return 0; 26018de8d7fSPeter Avalos } 26118de8d7fSPeter Avalos 2629f304aafSPeter Avalos void 2639f304aafSPeter Avalos ssh_kill_proxy_command(void) 2649f304aafSPeter Avalos { 2659f304aafSPeter Avalos /* 2669f304aafSPeter Avalos * Send SIGHUP to proxy command if used. We don't wait() in 2679f304aafSPeter Avalos * case it hangs and instead rely on init to reap the child 2689f304aafSPeter Avalos */ 2699f304aafSPeter Avalos if (proxy_command_pid > 1) 2709f304aafSPeter Avalos kill(proxy_command_pid, SIGHUP); 2719f304aafSPeter Avalos } 2729f304aafSPeter Avalos 27318de8d7fSPeter Avalos /* 27418de8d7fSPeter Avalos * Creates a (possibly privileged) socket for use as the ssh connection. 27518de8d7fSPeter Avalos */ 27618de8d7fSPeter Avalos static int 27718de8d7fSPeter Avalos ssh_create_socket(int privileged, struct addrinfo *ai) 27818de8d7fSPeter Avalos { 27936e94dc5SPeter Avalos int sock, r, gaierr; 28036e94dc5SPeter Avalos struct addrinfo hints, *res = NULL; 28118de8d7fSPeter Avalos 28218de8d7fSPeter Avalos sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 283856ea928SPeter Avalos if (sock < 0) { 28436e94dc5SPeter Avalos error("socket: %s", strerror(errno)); 285856ea928SPeter Avalos return -1; 286856ea928SPeter Avalos } 287856ea928SPeter Avalos fcntl(sock, F_SETFD, FD_CLOEXEC); 28818de8d7fSPeter Avalos 28918de8d7fSPeter Avalos /* Bind the socket to an alternative local IP address */ 29036e94dc5SPeter Avalos if (options.bind_address == NULL && !privileged) 29118de8d7fSPeter Avalos return sock; 29218de8d7fSPeter Avalos 29336e94dc5SPeter Avalos if (options.bind_address) { 29418de8d7fSPeter Avalos memset(&hints, 0, sizeof(hints)); 29518de8d7fSPeter Avalos hints.ai_family = ai->ai_family; 29618de8d7fSPeter Avalos hints.ai_socktype = ai->ai_socktype; 29718de8d7fSPeter Avalos hints.ai_protocol = ai->ai_protocol; 29818de8d7fSPeter Avalos hints.ai_flags = AI_PASSIVE; 29918de8d7fSPeter Avalos gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); 30018de8d7fSPeter Avalos if (gaierr) { 30118de8d7fSPeter Avalos error("getaddrinfo: %s: %s", options.bind_address, 30218de8d7fSPeter Avalos ssh_gai_strerror(gaierr)); 30318de8d7fSPeter Avalos close(sock); 30418de8d7fSPeter Avalos return -1; 30518de8d7fSPeter Avalos } 30636e94dc5SPeter Avalos } 30736e94dc5SPeter Avalos /* 30836e94dc5SPeter Avalos * If we are running as root and want to connect to a privileged 30936e94dc5SPeter Avalos * port, bind our own socket to a privileged port. 31036e94dc5SPeter Avalos */ 31136e94dc5SPeter Avalos if (privileged) { 31236e94dc5SPeter Avalos PRIV_START; 31336e94dc5SPeter Avalos r = bindresvport_sa(sock, res ? res->ai_addr : NULL); 31436e94dc5SPeter Avalos PRIV_END; 31536e94dc5SPeter Avalos if (r < 0) { 31636e94dc5SPeter Avalos error("bindresvport_sa: af=%d %s", ai->ai_family, 31736e94dc5SPeter Avalos strerror(errno)); 31836e94dc5SPeter Avalos goto fail; 31936e94dc5SPeter Avalos } 32036e94dc5SPeter Avalos } else { 32118de8d7fSPeter Avalos if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 32236e94dc5SPeter Avalos error("bind: %s: %s", options.bind_address, 32336e94dc5SPeter Avalos strerror(errno)); 32436e94dc5SPeter Avalos fail: 32518de8d7fSPeter Avalos close(sock); 32618de8d7fSPeter Avalos freeaddrinfo(res); 32718de8d7fSPeter Avalos return -1; 32818de8d7fSPeter Avalos } 32936e94dc5SPeter Avalos } 33036e94dc5SPeter Avalos if (res != NULL) 33118de8d7fSPeter Avalos freeaddrinfo(res); 33218de8d7fSPeter Avalos return sock; 33318de8d7fSPeter Avalos } 33418de8d7fSPeter Avalos 335*ce74bacaSMatthew Dillon /* 336*ce74bacaSMatthew Dillon * Wait up to *timeoutp milliseconds for fd to be readable. Updates 337*ce74bacaSMatthew Dillon * *timeoutp with time remaining. 338*ce74bacaSMatthew Dillon * Returns 0 if fd ready or -1 on timeout or error (see errno). 339*ce74bacaSMatthew Dillon */ 340*ce74bacaSMatthew Dillon static int 341*ce74bacaSMatthew Dillon waitrfd(int fd, int *timeoutp) 342*ce74bacaSMatthew Dillon { 343*ce74bacaSMatthew Dillon struct pollfd pfd; 344*ce74bacaSMatthew Dillon struct timeval t_start; 345*ce74bacaSMatthew Dillon int oerrno, r; 346*ce74bacaSMatthew Dillon 347*ce74bacaSMatthew Dillon gettimeofday(&t_start, NULL); 348*ce74bacaSMatthew Dillon pfd.fd = fd; 349*ce74bacaSMatthew Dillon pfd.events = POLLIN; 350*ce74bacaSMatthew Dillon for (; *timeoutp >= 0;) { 351*ce74bacaSMatthew Dillon r = poll(&pfd, 1, *timeoutp); 352*ce74bacaSMatthew Dillon oerrno = errno; 353*ce74bacaSMatthew Dillon ms_subtract_diff(&t_start, timeoutp); 354*ce74bacaSMatthew Dillon errno = oerrno; 355*ce74bacaSMatthew Dillon if (r > 0) 356*ce74bacaSMatthew Dillon return 0; 357*ce74bacaSMatthew Dillon else if (r == -1 && errno != EAGAIN) 358*ce74bacaSMatthew Dillon return -1; 359*ce74bacaSMatthew Dillon else if (r == 0) 360*ce74bacaSMatthew Dillon break; 361*ce74bacaSMatthew Dillon } 362*ce74bacaSMatthew Dillon /* timeout */ 363*ce74bacaSMatthew Dillon errno = ETIMEDOUT; 364*ce74bacaSMatthew Dillon return -1; 365*ce74bacaSMatthew Dillon } 366*ce74bacaSMatthew Dillon 36718de8d7fSPeter Avalos static int 36818de8d7fSPeter Avalos timeout_connect(int sockfd, const struct sockaddr *serv_addr, 36918de8d7fSPeter Avalos socklen_t addrlen, int *timeoutp) 37018de8d7fSPeter Avalos { 371*ce74bacaSMatthew Dillon int optval = 0; 372*ce74bacaSMatthew Dillon socklen_t optlen = sizeof(optval); 37318de8d7fSPeter Avalos 374*ce74bacaSMatthew Dillon /* No timeout: just do a blocking connect() */ 375*ce74bacaSMatthew Dillon if (*timeoutp <= 0) 376*ce74bacaSMatthew Dillon return connect(sockfd, serv_addr, addrlen); 37718de8d7fSPeter Avalos 37818de8d7fSPeter Avalos set_nonblock(sockfd); 379*ce74bacaSMatthew Dillon if (connect(sockfd, serv_addr, addrlen) == 0) { 380*ce74bacaSMatthew Dillon /* Succeeded already? */ 38118de8d7fSPeter Avalos unset_nonblock(sockfd); 382*ce74bacaSMatthew Dillon return 0; 383*ce74bacaSMatthew Dillon } else if (errno != EINPROGRESS) 384*ce74bacaSMatthew Dillon return -1; 38518de8d7fSPeter Avalos 386*ce74bacaSMatthew Dillon if (waitrfd(sockfd, timeoutp) == -1) 387*ce74bacaSMatthew Dillon return -1; 38818de8d7fSPeter Avalos 38918de8d7fSPeter Avalos /* Completed or failed */ 390*ce74bacaSMatthew Dillon if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { 39118de8d7fSPeter Avalos debug("getsockopt: %s", strerror(errno)); 392*ce74bacaSMatthew Dillon return -1; 39318de8d7fSPeter Avalos } 39418de8d7fSPeter Avalos if (optval != 0) { 39518de8d7fSPeter Avalos errno = optval; 396*ce74bacaSMatthew Dillon return -1; 39718de8d7fSPeter Avalos } 39818de8d7fSPeter Avalos unset_nonblock(sockfd); 399*ce74bacaSMatthew Dillon return 0; 40018de8d7fSPeter Avalos } 40118de8d7fSPeter Avalos 40218de8d7fSPeter Avalos /* 40318de8d7fSPeter Avalos * Opens a TCP/IP connection to the remote server on the given host. 40418de8d7fSPeter Avalos * The address of the remote host will be returned in hostaddr. 40518de8d7fSPeter Avalos * If port is 0, the default port will be used. If needpriv is true, 40618de8d7fSPeter Avalos * a privileged port will be allocated to make the connection. 40718de8d7fSPeter Avalos * This requires super-user privileges if needpriv is true. 40818de8d7fSPeter Avalos * Connection_attempts specifies the maximum number of tries (one per 40918de8d7fSPeter Avalos * second). If proxy_command is non-NULL, it specifies the command (with %h 41018de8d7fSPeter Avalos * and %p substituted for host and port, respectively) to use to contact 41118de8d7fSPeter Avalos * the daemon. 41218de8d7fSPeter Avalos */ 41336e94dc5SPeter Avalos static int 414*ce74bacaSMatthew Dillon ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop, 41536e94dc5SPeter Avalos struct sockaddr_storage *hostaddr, u_short port, int family, 41636e94dc5SPeter Avalos int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) 41718de8d7fSPeter Avalos { 41818de8d7fSPeter Avalos int on = 1; 41918de8d7fSPeter Avalos int sock = -1, attempt; 42018de8d7fSPeter Avalos char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 42136e94dc5SPeter Avalos struct addrinfo *ai; 42218de8d7fSPeter Avalos 423e9778795SPeter Avalos debug2("%s: needpriv %d", __func__, needpriv); 424e9778795SPeter Avalos memset(ntop, 0, sizeof(ntop)); 425e9778795SPeter Avalos memset(strport, 0, sizeof(strport)); 42618de8d7fSPeter Avalos 42718de8d7fSPeter Avalos for (attempt = 0; attempt < connection_attempts; attempt++) { 42818de8d7fSPeter Avalos if (attempt > 0) { 42918de8d7fSPeter Avalos /* Sleep a moment before retrying. */ 43018de8d7fSPeter Avalos sleep(1); 43118de8d7fSPeter Avalos debug("Trying again..."); 43218de8d7fSPeter Avalos } 43318de8d7fSPeter Avalos /* 43418de8d7fSPeter Avalos * Loop through addresses for this host, and try each one in 43518de8d7fSPeter Avalos * sequence until the connection succeeds. 43618de8d7fSPeter Avalos */ 43718de8d7fSPeter Avalos for (ai = aitop; ai; ai = ai->ai_next) { 43836e94dc5SPeter Avalos if (ai->ai_family != AF_INET && 43936e94dc5SPeter Avalos ai->ai_family != AF_INET6) 44018de8d7fSPeter Avalos continue; 44118de8d7fSPeter Avalos if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 44218de8d7fSPeter Avalos ntop, sizeof(ntop), strport, sizeof(strport), 44318de8d7fSPeter Avalos NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 444e9778795SPeter Avalos error("%s: getnameinfo failed", __func__); 44518de8d7fSPeter Avalos continue; 44618de8d7fSPeter Avalos } 44718de8d7fSPeter Avalos debug("Connecting to %.200s [%.100s] port %s.", 44818de8d7fSPeter Avalos host, ntop, strport); 44918de8d7fSPeter Avalos 45018de8d7fSPeter Avalos /* Create a socket for connecting. */ 45118de8d7fSPeter Avalos sock = ssh_create_socket(needpriv, ai); 45218de8d7fSPeter Avalos if (sock < 0) 45318de8d7fSPeter Avalos /* Any error is already output */ 45418de8d7fSPeter Avalos continue; 45518de8d7fSPeter Avalos 45618de8d7fSPeter Avalos if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 45718de8d7fSPeter Avalos timeout_ms) >= 0) { 45818de8d7fSPeter Avalos /* Successful connection. */ 45918de8d7fSPeter Avalos memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 46018de8d7fSPeter Avalos break; 46118de8d7fSPeter Avalos } else { 46218de8d7fSPeter Avalos debug("connect to address %s port %s: %s", 46318de8d7fSPeter Avalos ntop, strport, strerror(errno)); 46418de8d7fSPeter Avalos close(sock); 46518de8d7fSPeter Avalos sock = -1; 46618de8d7fSPeter Avalos } 46718de8d7fSPeter Avalos } 46818de8d7fSPeter Avalos if (sock != -1) 46918de8d7fSPeter Avalos break; /* Successful connection. */ 47018de8d7fSPeter Avalos } 47118de8d7fSPeter Avalos 47218de8d7fSPeter Avalos /* Return failure if we didn't get a successful connection. */ 47318de8d7fSPeter Avalos if (sock == -1) { 47418de8d7fSPeter Avalos error("ssh: connect to host %s port %s: %s", 47518de8d7fSPeter Avalos host, strport, strerror(errno)); 47618de8d7fSPeter Avalos return (-1); 47718de8d7fSPeter Avalos } 47818de8d7fSPeter Avalos 47918de8d7fSPeter Avalos debug("Connection established."); 48018de8d7fSPeter Avalos 48118de8d7fSPeter Avalos /* Set SO_KEEPALIVE if requested. */ 48218de8d7fSPeter Avalos if (want_keepalive && 48318de8d7fSPeter Avalos setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 48418de8d7fSPeter Avalos sizeof(on)) < 0) 48518de8d7fSPeter Avalos error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 48618de8d7fSPeter Avalos 48718de8d7fSPeter Avalos /* Set the connection. */ 488*ce74bacaSMatthew Dillon if (ssh_packet_set_connection(ssh, sock, sock) == NULL) 489*ce74bacaSMatthew Dillon return -1; /* ssh_packet_set_connection logs error */ 49018de8d7fSPeter Avalos 49118de8d7fSPeter Avalos return 0; 49218de8d7fSPeter Avalos } 49318de8d7fSPeter Avalos 49436e94dc5SPeter Avalos int 495*ce74bacaSMatthew Dillon ssh_connect(struct ssh *ssh, const char *host, struct addrinfo *addrs, 49636e94dc5SPeter Avalos struct sockaddr_storage *hostaddr, u_short port, int family, 49736e94dc5SPeter Avalos int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) 49836e94dc5SPeter Avalos { 49936e94dc5SPeter Avalos if (options.proxy_command == NULL) { 500*ce74bacaSMatthew Dillon return ssh_connect_direct(ssh, host, addrs, hostaddr, port, 501*ce74bacaSMatthew Dillon family, connection_attempts, timeout_ms, want_keepalive, 502*ce74bacaSMatthew Dillon needpriv); 50336e94dc5SPeter Avalos } else if (strcmp(options.proxy_command, "-") == 0) { 504*ce74bacaSMatthew Dillon if ((ssh_packet_set_connection(ssh, 505*ce74bacaSMatthew Dillon STDIN_FILENO, STDOUT_FILENO)) == NULL) 506*ce74bacaSMatthew Dillon return -1; /* ssh_packet_set_connection logs error */ 507*ce74bacaSMatthew Dillon return 0; 50836e94dc5SPeter Avalos } else if (options.proxy_use_fdpass) { 509*ce74bacaSMatthew Dillon return ssh_proxy_fdpass_connect(ssh, host, port, 51036e94dc5SPeter Avalos options.proxy_command); 51136e94dc5SPeter Avalos } 512*ce74bacaSMatthew Dillon return ssh_proxy_connect(ssh, host, port, options.proxy_command); 51336e94dc5SPeter Avalos } 51436e94dc5SPeter Avalos 51536e94dc5SPeter Avalos static void 51636e94dc5SPeter Avalos send_client_banner(int connection_out, int minor1) 51736e94dc5SPeter Avalos { 51836e94dc5SPeter Avalos /* Send our own protocol version identification. */ 51936e94dc5SPeter Avalos xasprintf(&client_version_string, "SSH-%d.%d-%.100s\r\n", 52036e94dc5SPeter Avalos PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION); 521e9778795SPeter Avalos if (atomicio(vwrite, connection_out, client_version_string, 52236e94dc5SPeter Avalos strlen(client_version_string)) != strlen(client_version_string)) 52336e94dc5SPeter Avalos fatal("write: %.100s", strerror(errno)); 52436e94dc5SPeter Avalos chop(client_version_string); 52536e94dc5SPeter Avalos debug("Local version string %.100s", client_version_string); 52636e94dc5SPeter Avalos } 52736e94dc5SPeter Avalos 52818de8d7fSPeter Avalos /* 52918de8d7fSPeter Avalos * Waits for the server identification string, and sends our own 53018de8d7fSPeter Avalos * identification string. 53118de8d7fSPeter Avalos */ 53240c002afSPeter Avalos void 53318de8d7fSPeter Avalos ssh_exchange_identification(int timeout_ms) 53418de8d7fSPeter Avalos { 53518de8d7fSPeter Avalos char buf[256], remote_version[256]; /* must be same size! */ 53618de8d7fSPeter Avalos int remote_major, remote_minor, mismatch; 53718de8d7fSPeter Avalos int connection_in = packet_get_connection_in(); 53818de8d7fSPeter Avalos int connection_out = packet_get_connection_out(); 53918de8d7fSPeter Avalos u_int i, n; 54018de8d7fSPeter Avalos size_t len; 541*ce74bacaSMatthew Dillon int rc; 54218de8d7fSPeter Avalos 54336e94dc5SPeter Avalos send_client_banner(connection_out, 0); 54436e94dc5SPeter Avalos 54518de8d7fSPeter Avalos /* Read other side's version identification. */ 54618de8d7fSPeter Avalos for (n = 0;;) { 54718de8d7fSPeter Avalos for (i = 0; i < sizeof(buf) - 1; i++) { 54818de8d7fSPeter Avalos if (timeout_ms > 0) { 549*ce74bacaSMatthew Dillon rc = waitrfd(connection_in, &timeout_ms); 550*ce74bacaSMatthew Dillon if (rc == -1 && errno == ETIMEDOUT) { 55118de8d7fSPeter Avalos fatal("Connection timed out during " 55218de8d7fSPeter Avalos "banner exchange"); 553*ce74bacaSMatthew Dillon } else if (rc == -1) { 554*ce74bacaSMatthew Dillon fatal("%s: %s", 555*ce74bacaSMatthew Dillon __func__, strerror(errno)); 55618de8d7fSPeter Avalos } 55718de8d7fSPeter Avalos } 55818de8d7fSPeter Avalos 559e9778795SPeter Avalos len = atomicio(read, connection_in, &buf[i], 1); 56018de8d7fSPeter Avalos if (len != 1 && errno == EPIPE) 56118de8d7fSPeter Avalos fatal("ssh_exchange_identification: " 56218de8d7fSPeter Avalos "Connection closed by remote host"); 56318de8d7fSPeter Avalos else if (len != 1) 56418de8d7fSPeter Avalos fatal("ssh_exchange_identification: " 56518de8d7fSPeter Avalos "read: %.100s", strerror(errno)); 56618de8d7fSPeter Avalos if (buf[i] == '\r') { 56718de8d7fSPeter Avalos buf[i] = '\n'; 56818de8d7fSPeter Avalos buf[i + 1] = 0; 56918de8d7fSPeter Avalos continue; /**XXX wait for \n */ 57018de8d7fSPeter Avalos } 57118de8d7fSPeter Avalos if (buf[i] == '\n') { 57218de8d7fSPeter Avalos buf[i + 1] = 0; 57318de8d7fSPeter Avalos break; 57418de8d7fSPeter Avalos } 57518de8d7fSPeter Avalos if (++n > 65536) 57618de8d7fSPeter Avalos fatal("ssh_exchange_identification: " 57718de8d7fSPeter Avalos "No banner received"); 57818de8d7fSPeter Avalos } 57918de8d7fSPeter Avalos buf[sizeof(buf) - 1] = 0; 58018de8d7fSPeter Avalos if (strncmp(buf, "SSH-", 4) == 0) 58118de8d7fSPeter Avalos break; 58218de8d7fSPeter Avalos debug("ssh_exchange_identification: %s", buf); 58318de8d7fSPeter Avalos } 58418de8d7fSPeter Avalos server_version_string = xstrdup(buf); 58518de8d7fSPeter Avalos 58618de8d7fSPeter Avalos /* 58718de8d7fSPeter Avalos * Check that the versions match. In future this might accept 58818de8d7fSPeter Avalos * several versions and set appropriate flags to handle them. 58918de8d7fSPeter Avalos */ 59018de8d7fSPeter Avalos if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 59118de8d7fSPeter Avalos &remote_major, &remote_minor, remote_version) != 3) 59218de8d7fSPeter Avalos fatal("Bad remote protocol version identification: '%.100s'", buf); 59318de8d7fSPeter Avalos debug("Remote protocol version %d.%d, remote software version %.100s", 59418de8d7fSPeter Avalos remote_major, remote_minor, remote_version); 59518de8d7fSPeter Avalos 596e9778795SPeter Avalos active_state->compat = compat_datafellows(remote_version); 59718de8d7fSPeter Avalos mismatch = 0; 59818de8d7fSPeter Avalos 59918de8d7fSPeter Avalos switch (remote_major) { 600*ce74bacaSMatthew Dillon case 2: 60118de8d7fSPeter Avalos break; 602*ce74bacaSMatthew Dillon case 1: 603*ce74bacaSMatthew Dillon if (remote_minor != 99) 60418de8d7fSPeter Avalos mismatch = 1; 60518de8d7fSPeter Avalos break; 60618de8d7fSPeter Avalos default: 60718de8d7fSPeter Avalos mismatch = 1; 60818de8d7fSPeter Avalos break; 60918de8d7fSPeter Avalos } 61018de8d7fSPeter Avalos if (mismatch) 61118de8d7fSPeter Avalos fatal("Protocol major versions differ: %d vs. %d", 612*ce74bacaSMatthew Dillon PROTOCOL_MAJOR_2, remote_major); 61336e94dc5SPeter Avalos if ((datafellows & SSH_BUG_DERIVEKEY) != 0) 61436e94dc5SPeter Avalos fatal("Server version \"%.100s\" uses unsafe key agreement; " 61536e94dc5SPeter Avalos "refusing connection", remote_version); 61636e94dc5SPeter Avalos if ((datafellows & SSH_BUG_RSASIGMD5) != 0) 61736e94dc5SPeter Avalos logit("Server version \"%.100s\" uses unsafe RSA signature " 61836e94dc5SPeter Avalos "scheme; disabling use of RSA keys", remote_version); 61918de8d7fSPeter Avalos chop(server_version_string); 62018de8d7fSPeter Avalos } 62118de8d7fSPeter Avalos 62218de8d7fSPeter Avalos /* defaults to 'no' */ 62318de8d7fSPeter Avalos static int 62418de8d7fSPeter Avalos confirm(const char *prompt) 62518de8d7fSPeter Avalos { 62618de8d7fSPeter Avalos const char *msg, *again = "Please type 'yes' or 'no': "; 62718de8d7fSPeter Avalos char *p; 62818de8d7fSPeter Avalos int ret = -1; 62918de8d7fSPeter Avalos 63018de8d7fSPeter Avalos if (options.batch_mode) 63118de8d7fSPeter Avalos return 0; 63218de8d7fSPeter Avalos for (msg = prompt;;msg = again) { 63318de8d7fSPeter Avalos p = read_passphrase(msg, RP_ECHO); 63418de8d7fSPeter Avalos if (p == NULL || 63518de8d7fSPeter Avalos (p[0] == '\0') || (p[0] == '\n') || 63618de8d7fSPeter Avalos strncasecmp(p, "no", 2) == 0) 63718de8d7fSPeter Avalos ret = 0; 63818de8d7fSPeter Avalos if (p && strncasecmp(p, "yes", 3) == 0) 63918de8d7fSPeter Avalos ret = 1; 64036e94dc5SPeter Avalos free(p); 64118de8d7fSPeter Avalos if (ret != -1) 64218de8d7fSPeter Avalos return ret; 64318de8d7fSPeter Avalos } 64418de8d7fSPeter Avalos } 64518de8d7fSPeter Avalos 646856ea928SPeter Avalos static int 647*ce74bacaSMatthew Dillon check_host_cert(const char *host, const struct sshkey *host_key) 648856ea928SPeter Avalos { 649856ea928SPeter Avalos const char *reason; 650856ea928SPeter Avalos 651856ea928SPeter Avalos if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { 652856ea928SPeter Avalos error("%s", reason); 653856ea928SPeter Avalos return 0; 654856ea928SPeter Avalos } 65536e94dc5SPeter Avalos if (buffer_len(host_key->cert->critical) != 0) { 656856ea928SPeter Avalos error("Certificate for %s contains unsupported " 657856ea928SPeter Avalos "critical options(s)", host); 658856ea928SPeter Avalos return 0; 659856ea928SPeter Avalos } 660856ea928SPeter Avalos return 1; 661856ea928SPeter Avalos } 662856ea928SPeter Avalos 6639f304aafSPeter Avalos static int 6649f304aafSPeter Avalos sockaddr_is_local(struct sockaddr *hostaddr) 6659f304aafSPeter Avalos { 6669f304aafSPeter Avalos switch (hostaddr->sa_family) { 6679f304aafSPeter Avalos case AF_INET: 6689f304aafSPeter Avalos return (ntohl(((struct sockaddr_in *)hostaddr)-> 6699f304aafSPeter Avalos sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 6709f304aafSPeter Avalos case AF_INET6: 6719f304aafSPeter Avalos return IN6_IS_ADDR_LOOPBACK( 6729f304aafSPeter Avalos &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 6739f304aafSPeter Avalos default: 6749f304aafSPeter Avalos return 0; 6759f304aafSPeter Avalos } 6769f304aafSPeter Avalos } 6779f304aafSPeter Avalos 6789f304aafSPeter Avalos /* 6799f304aafSPeter Avalos * Prepare the hostname and ip address strings that are used to lookup 6809f304aafSPeter Avalos * host keys in known_hosts files. These may have a port number appended. 6819f304aafSPeter Avalos */ 6829f304aafSPeter Avalos void 6839f304aafSPeter Avalos get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, 6849f304aafSPeter Avalos u_short port, char **hostfile_hostname, char **hostfile_ipaddr) 6859f304aafSPeter Avalos { 6869f304aafSPeter Avalos char ntop[NI_MAXHOST]; 6879f304aafSPeter Avalos socklen_t addrlen; 6889f304aafSPeter Avalos 6899f304aafSPeter Avalos switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { 6909f304aafSPeter Avalos case -1: 6919f304aafSPeter Avalos addrlen = 0; 6929f304aafSPeter Avalos break; 6939f304aafSPeter Avalos case AF_INET: 6949f304aafSPeter Avalos addrlen = sizeof(struct sockaddr_in); 6959f304aafSPeter Avalos break; 6969f304aafSPeter Avalos case AF_INET6: 6979f304aafSPeter Avalos addrlen = sizeof(struct sockaddr_in6); 6989f304aafSPeter Avalos break; 6999f304aafSPeter Avalos default: 7009f304aafSPeter Avalos addrlen = sizeof(struct sockaddr); 7019f304aafSPeter Avalos break; 7029f304aafSPeter Avalos } 7039f304aafSPeter Avalos 7049f304aafSPeter Avalos /* 7059f304aafSPeter Avalos * We don't have the remote ip-address for connections 7069f304aafSPeter Avalos * using a proxy command 7079f304aafSPeter Avalos */ 7089f304aafSPeter Avalos if (hostfile_ipaddr != NULL) { 7099f304aafSPeter Avalos if (options.proxy_command == NULL) { 7109f304aafSPeter Avalos if (getnameinfo(hostaddr, addrlen, 7119f304aafSPeter Avalos ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) 712e9778795SPeter Avalos fatal("%s: getnameinfo failed", __func__); 7139f304aafSPeter Avalos *hostfile_ipaddr = put_host_port(ntop, port); 7149f304aafSPeter Avalos } else { 7159f304aafSPeter Avalos *hostfile_ipaddr = xstrdup("<no hostip for proxy " 7169f304aafSPeter Avalos "command>"); 7179f304aafSPeter Avalos } 7189f304aafSPeter Avalos } 7199f304aafSPeter Avalos 7209f304aafSPeter Avalos /* 7219f304aafSPeter Avalos * Allow the user to record the key under a different name or 7229f304aafSPeter Avalos * differentiate a non-standard port. This is useful for ssh 7239f304aafSPeter Avalos * tunneling over forwarded connections or if you run multiple 7249f304aafSPeter Avalos * sshd's on different ports on the same machine. 7259f304aafSPeter Avalos */ 7269f304aafSPeter Avalos if (hostfile_hostname != NULL) { 7279f304aafSPeter Avalos if (options.host_key_alias != NULL) { 7289f304aafSPeter Avalos *hostfile_hostname = xstrdup(options.host_key_alias); 7299f304aafSPeter Avalos debug("using hostkeyalias: %s", *hostfile_hostname); 7309f304aafSPeter Avalos } else { 7319f304aafSPeter Avalos *hostfile_hostname = put_host_port(hostname, port); 7329f304aafSPeter Avalos } 7339f304aafSPeter Avalos } 7349f304aafSPeter Avalos } 7359f304aafSPeter Avalos 73618de8d7fSPeter Avalos /* 73718de8d7fSPeter Avalos * check whether the supplied host key is valid, return -1 if the key 7381c188a7fSPeter Avalos * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. 73918de8d7fSPeter Avalos */ 74018de8d7fSPeter Avalos #define RDRW 0 74118de8d7fSPeter Avalos #define RDONLY 1 74218de8d7fSPeter Avalos #define ROQUIET 2 74318de8d7fSPeter Avalos static int 74418de8d7fSPeter Avalos check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 745*ce74bacaSMatthew Dillon struct sshkey *host_key, int readonly, 7461c188a7fSPeter Avalos char **user_hostfiles, u_int num_user_hostfiles, 7471c188a7fSPeter Avalos char **system_hostfiles, u_int num_system_hostfiles) 74818de8d7fSPeter Avalos { 74918de8d7fSPeter Avalos HostStatus host_status; 75018de8d7fSPeter Avalos HostStatus ip_status; 751*ce74bacaSMatthew Dillon struct sshkey *raw_key = NULL; 7521c188a7fSPeter Avalos char *ip = NULL, *host = NULL; 7531c188a7fSPeter Avalos char hostline[1000], *hostp, *fp, *ra; 75418de8d7fSPeter Avalos char msg[1024]; 7551c188a7fSPeter Avalos const char *type; 7569f304aafSPeter Avalos const struct hostkey_entry *host_found, *ip_found; 7571c188a7fSPeter Avalos int len, cancelled_forwarding = 0; 7581c188a7fSPeter Avalos int local = sockaddr_is_local(hostaddr); 759*ce74bacaSMatthew Dillon int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0; 760e9778795SPeter Avalos int hostkey_trusted = 0; /* Known or explicitly accepted by user */ 7611c188a7fSPeter Avalos struct hostkeys *host_hostkeys, *ip_hostkeys; 7621c188a7fSPeter Avalos u_int i; 76318de8d7fSPeter Avalos 76418de8d7fSPeter Avalos /* 76518de8d7fSPeter Avalos * Force accepting of the host key for loopback/localhost. The 76618de8d7fSPeter Avalos * problem is that if the home directory is NFS-mounted to multiple 76718de8d7fSPeter Avalos * machines, localhost will refer to a different machine in each of 76818de8d7fSPeter Avalos * them, and the user will get bogus HOST_CHANGED warnings. This 76918de8d7fSPeter Avalos * essentially disables host authentication for localhost; however, 77018de8d7fSPeter Avalos * this is probably not a real problem. 77118de8d7fSPeter Avalos */ 77218de8d7fSPeter Avalos if (options.no_host_authentication_for_localhost == 1 && local && 77318de8d7fSPeter Avalos options.host_key_alias == NULL) { 77418de8d7fSPeter Avalos debug("Forcing accepting of host key for " 77518de8d7fSPeter Avalos "loopback/localhost."); 77618de8d7fSPeter Avalos return 0; 77718de8d7fSPeter Avalos } 77818de8d7fSPeter Avalos 77918de8d7fSPeter Avalos /* 7809f304aafSPeter Avalos * Prepare the hostname and address strings used for hostkey lookup. 7819f304aafSPeter Avalos * In some cases, these will have a port number appended. 78218de8d7fSPeter Avalos */ 7839f304aafSPeter Avalos get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); 78418de8d7fSPeter Avalos 78518de8d7fSPeter Avalos /* 78618de8d7fSPeter Avalos * Turn off check_host_ip if the connection is to localhost, via proxy 78718de8d7fSPeter Avalos * command or if we don't have a hostname to compare with 78818de8d7fSPeter Avalos */ 78918de8d7fSPeter Avalos if (options.check_host_ip && (local || 79018de8d7fSPeter Avalos strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 79118de8d7fSPeter Avalos options.check_host_ip = 0; 79218de8d7fSPeter Avalos 7939f304aafSPeter Avalos host_hostkeys = init_hostkeys(); 7941c188a7fSPeter Avalos for (i = 0; i < num_user_hostfiles; i++) 7951c188a7fSPeter Avalos load_hostkeys(host_hostkeys, host, user_hostfiles[i]); 7961c188a7fSPeter Avalos for (i = 0; i < num_system_hostfiles; i++) 7971c188a7fSPeter Avalos load_hostkeys(host_hostkeys, host, system_hostfiles[i]); 7989f304aafSPeter Avalos 7999f304aafSPeter Avalos ip_hostkeys = NULL; 8009f304aafSPeter Avalos if (!want_cert && options.check_host_ip) { 8019f304aafSPeter Avalos ip_hostkeys = init_hostkeys(); 8021c188a7fSPeter Avalos for (i = 0; i < num_user_hostfiles; i++) 8031c188a7fSPeter Avalos load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]); 8041c188a7fSPeter Avalos for (i = 0; i < num_system_hostfiles; i++) 8051c188a7fSPeter Avalos load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]); 80618de8d7fSPeter Avalos } 80718de8d7fSPeter Avalos 808856ea928SPeter Avalos retry: 8099f304aafSPeter Avalos /* Reload these as they may have changed on cert->key downgrade */ 810*ce74bacaSMatthew Dillon want_cert = sshkey_is_cert(host_key); 811*ce74bacaSMatthew Dillon type = sshkey_type(host_key); 812856ea928SPeter Avalos 81318de8d7fSPeter Avalos /* 81418de8d7fSPeter Avalos * Check if the host key is present in the user's list of known 81518de8d7fSPeter Avalos * hosts or in the systemwide list. 81618de8d7fSPeter Avalos */ 8179f304aafSPeter Avalos host_status = check_key_in_hostkeys(host_hostkeys, host_key, 8189f304aafSPeter Avalos &host_found); 8199f304aafSPeter Avalos 82018de8d7fSPeter Avalos /* 82118de8d7fSPeter Avalos * Also perform check for the ip address, skip the check if we are 822856ea928SPeter Avalos * localhost, looking for a certificate, or the hostname was an ip 823856ea928SPeter Avalos * address to begin with. 82418de8d7fSPeter Avalos */ 8259f304aafSPeter Avalos if (!want_cert && ip_hostkeys != NULL) { 8269f304aafSPeter Avalos ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, 8279f304aafSPeter Avalos &ip_found); 82818de8d7fSPeter Avalos if (host_status == HOST_CHANGED && 8299f304aafSPeter Avalos (ip_status != HOST_CHANGED || 8309f304aafSPeter Avalos (ip_found != NULL && 831*ce74bacaSMatthew Dillon !sshkey_equal(ip_found->key, host_found->key)))) 83218de8d7fSPeter Avalos host_ip_differ = 1; 83318de8d7fSPeter Avalos } else 83418de8d7fSPeter Avalos ip_status = host_status; 83518de8d7fSPeter Avalos 83618de8d7fSPeter Avalos switch (host_status) { 83718de8d7fSPeter Avalos case HOST_OK: 83818de8d7fSPeter Avalos /* The host is known and the key matches. */ 839856ea928SPeter Avalos debug("Host '%.200s' is known and matches the %s host %s.", 840856ea928SPeter Avalos host, type, want_cert ? "certificate" : "key"); 8419f304aafSPeter Avalos debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", 8429f304aafSPeter Avalos host_found->file, host_found->line); 843*ce74bacaSMatthew Dillon if (want_cert && 844*ce74bacaSMatthew Dillon !check_host_cert(options.host_key_alias == NULL ? 845*ce74bacaSMatthew Dillon hostname : options.host_key_alias, host_key)) 846856ea928SPeter Avalos goto fail; 84718de8d7fSPeter Avalos if (options.check_host_ip && ip_status == HOST_NEW) { 848856ea928SPeter Avalos if (readonly || want_cert) 84918de8d7fSPeter Avalos logit("%s host key for IP address " 85018de8d7fSPeter Avalos "'%.128s' not in list of known hosts.", 85118de8d7fSPeter Avalos type, ip); 8521c188a7fSPeter Avalos else if (!add_host_to_hostfile(user_hostfiles[0], ip, 85318de8d7fSPeter Avalos host_key, options.hash_known_hosts)) 85418de8d7fSPeter Avalos logit("Failed to add the %s host key for IP " 85518de8d7fSPeter Avalos "address '%.128s' to the list of known " 856e9778795SPeter Avalos "hosts (%.500s).", type, ip, 8571c188a7fSPeter Avalos user_hostfiles[0]); 85818de8d7fSPeter Avalos else 85918de8d7fSPeter Avalos logit("Warning: Permanently added the %s host " 86018de8d7fSPeter Avalos "key for IP address '%.128s' to the list " 86118de8d7fSPeter Avalos "of known hosts.", type, ip); 86218de8d7fSPeter Avalos } else if (options.visual_host_key) { 863e9778795SPeter Avalos fp = sshkey_fingerprint(host_key, 864e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT); 865e9778795SPeter Avalos ra = sshkey_fingerprint(host_key, 866e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_RANDOMART); 867e9778795SPeter Avalos if (fp == NULL || ra == NULL) 868e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 869e9778795SPeter Avalos logit("Host key fingerprint is %s\n%s", fp, ra); 87036e94dc5SPeter Avalos free(ra); 87136e94dc5SPeter Avalos free(fp); 87218de8d7fSPeter Avalos } 873e9778795SPeter Avalos hostkey_trusted = 1; 87418de8d7fSPeter Avalos break; 87518de8d7fSPeter Avalos case HOST_NEW: 87618de8d7fSPeter Avalos if (options.host_key_alias == NULL && port != 0 && 87718de8d7fSPeter Avalos port != SSH_DEFAULT_PORT) { 87818de8d7fSPeter Avalos debug("checking without port identifier"); 879cb5eb4f1SPeter Avalos if (check_host_key(hostname, hostaddr, 0, host_key, 8801c188a7fSPeter Avalos ROQUIET, user_hostfiles, num_user_hostfiles, 8811c188a7fSPeter Avalos system_hostfiles, num_system_hostfiles) == 0) { 88218de8d7fSPeter Avalos debug("found matching key w/out port"); 88318de8d7fSPeter Avalos break; 88418de8d7fSPeter Avalos } 88518de8d7fSPeter Avalos } 886856ea928SPeter Avalos if (readonly || want_cert) 88718de8d7fSPeter Avalos goto fail; 88818de8d7fSPeter Avalos /* The host is new. */ 889*ce74bacaSMatthew Dillon if (options.strict_host_key_checking == 890*ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_YES) { 89118de8d7fSPeter Avalos /* 89218de8d7fSPeter Avalos * User has requested strict host key checking. We 89318de8d7fSPeter Avalos * will not add the host key automatically. The only 89418de8d7fSPeter Avalos * alternative left is to abort. 89518de8d7fSPeter Avalos */ 89618de8d7fSPeter Avalos error("No %s host key is known for %.200s and you " 89718de8d7fSPeter Avalos "have requested strict checking.", type, host); 89818de8d7fSPeter Avalos goto fail; 899*ce74bacaSMatthew Dillon } else if (options.strict_host_key_checking == 900*ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_ASK) { 90118de8d7fSPeter Avalos char msg1[1024], msg2[1024]; 90218de8d7fSPeter Avalos 9039f304aafSPeter Avalos if (show_other_keys(host_hostkeys, host_key)) 90418de8d7fSPeter Avalos snprintf(msg1, sizeof(msg1), 90518de8d7fSPeter Avalos "\nbut keys of different type are already" 90618de8d7fSPeter Avalos " known for this host."); 90718de8d7fSPeter Avalos else 90818de8d7fSPeter Avalos snprintf(msg1, sizeof(msg1), "."); 90918de8d7fSPeter Avalos /* The default */ 910e9778795SPeter Avalos fp = sshkey_fingerprint(host_key, 911e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT); 912e9778795SPeter Avalos ra = sshkey_fingerprint(host_key, 913e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_RANDOMART); 914e9778795SPeter Avalos if (fp == NULL || ra == NULL) 915e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 91618de8d7fSPeter Avalos msg2[0] = '\0'; 91718de8d7fSPeter Avalos if (options.verify_host_key_dns) { 91818de8d7fSPeter Avalos if (matching_host_key_dns) 91918de8d7fSPeter Avalos snprintf(msg2, sizeof(msg2), 92018de8d7fSPeter Avalos "Matching host key fingerprint" 92118de8d7fSPeter Avalos " found in DNS.\n"); 92218de8d7fSPeter Avalos else 92318de8d7fSPeter Avalos snprintf(msg2, sizeof(msg2), 92418de8d7fSPeter Avalos "No matching host key fingerprint" 92518de8d7fSPeter Avalos " found in DNS.\n"); 92618de8d7fSPeter Avalos } 92718de8d7fSPeter Avalos snprintf(msg, sizeof(msg), 92818de8d7fSPeter Avalos "The authenticity of host '%.200s (%s)' can't be " 92918de8d7fSPeter Avalos "established%s\n" 93018de8d7fSPeter Avalos "%s key fingerprint is %s.%s%s\n%s" 93118de8d7fSPeter Avalos "Are you sure you want to continue connecting " 93218de8d7fSPeter Avalos "(yes/no)? ", 93318de8d7fSPeter Avalos host, ip, msg1, type, fp, 93418de8d7fSPeter Avalos options.visual_host_key ? "\n" : "", 93518de8d7fSPeter Avalos options.visual_host_key ? ra : "", 93618de8d7fSPeter Avalos msg2); 93736e94dc5SPeter Avalos free(ra); 93836e94dc5SPeter Avalos free(fp); 93918de8d7fSPeter Avalos if (!confirm(msg)) 94018de8d7fSPeter Avalos goto fail; 941e9778795SPeter Avalos hostkey_trusted = 1; /* user explicitly confirmed */ 94218de8d7fSPeter Avalos } 94318de8d7fSPeter Avalos /* 944*ce74bacaSMatthew Dillon * If in "new" or "off" strict mode, add the key automatically 945*ce74bacaSMatthew Dillon * to the local known_hosts file. 94618de8d7fSPeter Avalos */ 94718de8d7fSPeter Avalos if (options.check_host_ip && ip_status == HOST_NEW) { 9489f304aafSPeter Avalos snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 94918de8d7fSPeter Avalos hostp = hostline; 95018de8d7fSPeter Avalos if (options.hash_known_hosts) { 95118de8d7fSPeter Avalos /* Add hash of host and IP separately */ 9521c188a7fSPeter Avalos r = add_host_to_hostfile(user_hostfiles[0], 9531c188a7fSPeter Avalos host, host_key, options.hash_known_hosts) && 9541c188a7fSPeter Avalos add_host_to_hostfile(user_hostfiles[0], ip, 95518de8d7fSPeter Avalos host_key, options.hash_known_hosts); 95618de8d7fSPeter Avalos } else { 95718de8d7fSPeter Avalos /* Add unhashed "host,ip" */ 9581c188a7fSPeter Avalos r = add_host_to_hostfile(user_hostfiles[0], 95918de8d7fSPeter Avalos hostline, host_key, 96018de8d7fSPeter Avalos options.hash_known_hosts); 96118de8d7fSPeter Avalos } 96218de8d7fSPeter Avalos } else { 9631c188a7fSPeter Avalos r = add_host_to_hostfile(user_hostfiles[0], host, 9641c188a7fSPeter Avalos host_key, options.hash_known_hosts); 96518de8d7fSPeter Avalos hostp = host; 96618de8d7fSPeter Avalos } 96718de8d7fSPeter Avalos 96818de8d7fSPeter Avalos if (!r) 96918de8d7fSPeter Avalos logit("Failed to add the host to the list of known " 9701c188a7fSPeter Avalos "hosts (%.500s).", user_hostfiles[0]); 97118de8d7fSPeter Avalos else 97218de8d7fSPeter Avalos logit("Warning: Permanently added '%.200s' (%s) to the " 97318de8d7fSPeter Avalos "list of known hosts.", hostp, type); 97418de8d7fSPeter Avalos break; 975856ea928SPeter Avalos case HOST_REVOKED: 976856ea928SPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 977856ea928SPeter Avalos error("@ WARNING: REVOKED HOST KEY DETECTED! @"); 978856ea928SPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 979856ea928SPeter Avalos error("The %s host key for %s is marked as revoked.", type, host); 980856ea928SPeter Avalos error("This could mean that a stolen key is being used to"); 981856ea928SPeter Avalos error("impersonate this host."); 982856ea928SPeter Avalos 983856ea928SPeter Avalos /* 984856ea928SPeter Avalos * If strict host key checking is in use, the user will have 985856ea928SPeter Avalos * to edit the key manually and we can only abort. 986856ea928SPeter Avalos */ 987*ce74bacaSMatthew Dillon if (options.strict_host_key_checking != 988*ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_OFF) { 989856ea928SPeter Avalos error("%s host key for %.200s was revoked and you have " 990856ea928SPeter Avalos "requested strict checking.", type, host); 991856ea928SPeter Avalos goto fail; 992856ea928SPeter Avalos } 993856ea928SPeter Avalos goto continue_unsafe; 994856ea928SPeter Avalos 99518de8d7fSPeter Avalos case HOST_CHANGED: 996856ea928SPeter Avalos if (want_cert) { 997856ea928SPeter Avalos /* 998856ea928SPeter Avalos * This is only a debug() since it is valid to have 999856ea928SPeter Avalos * CAs with wildcard DNS matches that don't match 1000856ea928SPeter Avalos * all hosts that one might visit. 1001856ea928SPeter Avalos */ 1002856ea928SPeter Avalos debug("Host certificate authority does not " 10039f304aafSPeter Avalos "match %s in %s:%lu", CA_MARKER, 10049f304aafSPeter Avalos host_found->file, host_found->line); 1005856ea928SPeter Avalos goto fail; 1006856ea928SPeter Avalos } 100718de8d7fSPeter Avalos if (readonly == ROQUIET) 100818de8d7fSPeter Avalos goto fail; 100918de8d7fSPeter Avalos if (options.check_host_ip && host_ip_differ) { 101018de8d7fSPeter Avalos char *key_msg; 101118de8d7fSPeter Avalos if (ip_status == HOST_NEW) 101218de8d7fSPeter Avalos key_msg = "is unknown"; 101318de8d7fSPeter Avalos else if (ip_status == HOST_OK) 101418de8d7fSPeter Avalos key_msg = "is unchanged"; 101518de8d7fSPeter Avalos else 101618de8d7fSPeter Avalos key_msg = "has a different value"; 101718de8d7fSPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 101818de8d7fSPeter Avalos error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 101918de8d7fSPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 102018de8d7fSPeter Avalos error("The %s host key for %s has changed,", type, host); 102118de8d7fSPeter Avalos error("and the key for the corresponding IP address %s", ip); 102218de8d7fSPeter Avalos error("%s. This could either mean that", key_msg); 102318de8d7fSPeter Avalos error("DNS SPOOFING is happening or the IP address for the host"); 102418de8d7fSPeter Avalos error("and its host key have changed at the same time."); 102518de8d7fSPeter Avalos if (ip_status != HOST_NEW) 10269f304aafSPeter Avalos error("Offending key for IP in %s:%lu", 10279f304aafSPeter Avalos ip_found->file, ip_found->line); 102818de8d7fSPeter Avalos } 102918de8d7fSPeter Avalos /* The host key has changed. */ 103018de8d7fSPeter Avalos warn_changed_key(host_key); 103118de8d7fSPeter Avalos error("Add correct host key in %.100s to get rid of this message.", 10321c188a7fSPeter Avalos user_hostfiles[0]); 1033*ce74bacaSMatthew Dillon error("Offending %s key in %s:%lu", 1034*ce74bacaSMatthew Dillon sshkey_type(host_found->key), 10359f304aafSPeter Avalos host_found->file, host_found->line); 103618de8d7fSPeter Avalos 103718de8d7fSPeter Avalos /* 103818de8d7fSPeter Avalos * If strict host key checking is in use, the user will have 103918de8d7fSPeter Avalos * to edit the key manually and we can only abort. 104018de8d7fSPeter Avalos */ 1041*ce74bacaSMatthew Dillon if (options.strict_host_key_checking != 1042*ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_OFF) { 104318de8d7fSPeter Avalos error("%s host key for %.200s has changed and you have " 104418de8d7fSPeter Avalos "requested strict checking.", type, host); 104518de8d7fSPeter Avalos goto fail; 104618de8d7fSPeter Avalos } 104718de8d7fSPeter Avalos 1048856ea928SPeter Avalos continue_unsafe: 104918de8d7fSPeter Avalos /* 105018de8d7fSPeter Avalos * If strict host key checking has not been requested, allow 105118de8d7fSPeter Avalos * the connection but without MITM-able authentication or 105218de8d7fSPeter Avalos * forwarding. 105318de8d7fSPeter Avalos */ 105418de8d7fSPeter Avalos if (options.password_authentication) { 105518de8d7fSPeter Avalos error("Password authentication is disabled to avoid " 105618de8d7fSPeter Avalos "man-in-the-middle attacks."); 105718de8d7fSPeter Avalos options.password_authentication = 0; 105818de8d7fSPeter Avalos cancelled_forwarding = 1; 105918de8d7fSPeter Avalos } 106018de8d7fSPeter Avalos if (options.kbd_interactive_authentication) { 106118de8d7fSPeter Avalos error("Keyboard-interactive authentication is disabled" 106218de8d7fSPeter Avalos " to avoid man-in-the-middle attacks."); 106318de8d7fSPeter Avalos options.kbd_interactive_authentication = 0; 106418de8d7fSPeter Avalos options.challenge_response_authentication = 0; 106518de8d7fSPeter Avalos cancelled_forwarding = 1; 106618de8d7fSPeter Avalos } 106718de8d7fSPeter Avalos if (options.challenge_response_authentication) { 106818de8d7fSPeter Avalos error("Challenge/response authentication is disabled" 106918de8d7fSPeter Avalos " to avoid man-in-the-middle attacks."); 107018de8d7fSPeter Avalos options.challenge_response_authentication = 0; 107118de8d7fSPeter Avalos cancelled_forwarding = 1; 107218de8d7fSPeter Avalos } 107318de8d7fSPeter Avalos if (options.forward_agent) { 107418de8d7fSPeter Avalos error("Agent forwarding is disabled to avoid " 107518de8d7fSPeter Avalos "man-in-the-middle attacks."); 107618de8d7fSPeter Avalos options.forward_agent = 0; 107718de8d7fSPeter Avalos cancelled_forwarding = 1; 107818de8d7fSPeter Avalos } 107918de8d7fSPeter Avalos if (options.forward_x11) { 108018de8d7fSPeter Avalos error("X11 forwarding is disabled to avoid " 108118de8d7fSPeter Avalos "man-in-the-middle attacks."); 108218de8d7fSPeter Avalos options.forward_x11 = 0; 108318de8d7fSPeter Avalos cancelled_forwarding = 1; 108418de8d7fSPeter Avalos } 108518de8d7fSPeter Avalos if (options.num_local_forwards > 0 || 108618de8d7fSPeter Avalos options.num_remote_forwards > 0) { 108718de8d7fSPeter Avalos error("Port forwarding is disabled to avoid " 108818de8d7fSPeter Avalos "man-in-the-middle attacks."); 108918de8d7fSPeter Avalos options.num_local_forwards = 109018de8d7fSPeter Avalos options.num_remote_forwards = 0; 109118de8d7fSPeter Avalos cancelled_forwarding = 1; 109218de8d7fSPeter Avalos } 109318de8d7fSPeter Avalos if (options.tun_open != SSH_TUNMODE_NO) { 109418de8d7fSPeter Avalos error("Tunnel forwarding is disabled to avoid " 109518de8d7fSPeter Avalos "man-in-the-middle attacks."); 109618de8d7fSPeter Avalos options.tun_open = SSH_TUNMODE_NO; 109718de8d7fSPeter Avalos cancelled_forwarding = 1; 109818de8d7fSPeter Avalos } 109918de8d7fSPeter Avalos if (options.exit_on_forward_failure && cancelled_forwarding) 110018de8d7fSPeter Avalos fatal("Error: forwarding disabled due to host key " 110118de8d7fSPeter Avalos "check failure"); 110218de8d7fSPeter Avalos 110318de8d7fSPeter Avalos /* 110418de8d7fSPeter Avalos * XXX Should permit the user to change to use the new id. 110518de8d7fSPeter Avalos * This could be done by converting the host key to an 110618de8d7fSPeter Avalos * identifying sentence, tell that the host identifies itself 1107856ea928SPeter Avalos * by that sentence, and ask the user if he/she wishes to 110818de8d7fSPeter Avalos * accept the authentication. 110918de8d7fSPeter Avalos */ 111018de8d7fSPeter Avalos break; 111118de8d7fSPeter Avalos case HOST_FOUND: 111218de8d7fSPeter Avalos fatal("internal error"); 111318de8d7fSPeter Avalos break; 111418de8d7fSPeter Avalos } 111518de8d7fSPeter Avalos 111618de8d7fSPeter Avalos if (options.check_host_ip && host_status != HOST_CHANGED && 111718de8d7fSPeter Avalos ip_status == HOST_CHANGED) { 111818de8d7fSPeter Avalos snprintf(msg, sizeof(msg), 111918de8d7fSPeter Avalos "Warning: the %s host key for '%.200s' " 112018de8d7fSPeter Avalos "differs from the key for the IP address '%.128s'" 11219f304aafSPeter Avalos "\nOffending key for IP in %s:%lu", 11229f304aafSPeter Avalos type, host, ip, ip_found->file, ip_found->line); 112318de8d7fSPeter Avalos if (host_status == HOST_OK) { 112418de8d7fSPeter Avalos len = strlen(msg); 112518de8d7fSPeter Avalos snprintf(msg + len, sizeof(msg) - len, 11269f304aafSPeter Avalos "\nMatching host key in %s:%lu", 11279f304aafSPeter Avalos host_found->file, host_found->line); 112818de8d7fSPeter Avalos } 1129*ce74bacaSMatthew Dillon if (options.strict_host_key_checking == 1130*ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_ASK) { 113118de8d7fSPeter Avalos strlcat(msg, "\nAre you sure you want " 113218de8d7fSPeter Avalos "to continue connecting (yes/no)? ", sizeof(msg)); 113318de8d7fSPeter Avalos if (!confirm(msg)) 113418de8d7fSPeter Avalos goto fail; 1135*ce74bacaSMatthew Dillon } else if (options.strict_host_key_checking != 1136*ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_OFF) { 1137*ce74bacaSMatthew Dillon logit("%s", msg); 1138*ce74bacaSMatthew Dillon error("Exiting, you have requested strict checking."); 1139*ce74bacaSMatthew Dillon goto fail; 114018de8d7fSPeter Avalos } else { 114118de8d7fSPeter Avalos logit("%s", msg); 114218de8d7fSPeter Avalos } 114318de8d7fSPeter Avalos } 114418de8d7fSPeter Avalos 1145e9778795SPeter Avalos if (!hostkey_trusted && options.update_hostkeys) { 1146e9778795SPeter Avalos debug("%s: hostkey not known or explicitly trusted: " 1147e9778795SPeter Avalos "disabling UpdateHostkeys", __func__); 1148e9778795SPeter Avalos options.update_hostkeys = 0; 1149e9778795SPeter Avalos } 1150e9778795SPeter Avalos 115136e94dc5SPeter Avalos free(ip); 115236e94dc5SPeter Avalos free(host); 11539f304aafSPeter Avalos if (host_hostkeys != NULL) 11549f304aafSPeter Avalos free_hostkeys(host_hostkeys); 11559f304aafSPeter Avalos if (ip_hostkeys != NULL) 11569f304aafSPeter Avalos free_hostkeys(ip_hostkeys); 115718de8d7fSPeter Avalos return 0; 115818de8d7fSPeter Avalos 115918de8d7fSPeter Avalos fail: 1160856ea928SPeter Avalos if (want_cert && host_status != HOST_REVOKED) { 1161856ea928SPeter Avalos /* 1162856ea928SPeter Avalos * No matching certificate. Downgrade cert to raw key and 1163856ea928SPeter Avalos * search normally. 1164856ea928SPeter Avalos */ 1165856ea928SPeter Avalos debug("No matching CA found. Retry with plain key"); 1166*ce74bacaSMatthew Dillon if ((r = sshkey_from_private(host_key, &raw_key)) != 0) 1167*ce74bacaSMatthew Dillon fatal("%s: sshkey_from_private: %s", 1168*ce74bacaSMatthew Dillon __func__, ssh_err(r)); 1169*ce74bacaSMatthew Dillon if ((r = sshkey_drop_cert(raw_key)) != 0) 1170*ce74bacaSMatthew Dillon fatal("Couldn't drop certificate: %s", ssh_err(r)); 1171856ea928SPeter Avalos host_key = raw_key; 1172856ea928SPeter Avalos goto retry; 1173856ea928SPeter Avalos } 1174856ea928SPeter Avalos if (raw_key != NULL) 1175*ce74bacaSMatthew Dillon sshkey_free(raw_key); 117636e94dc5SPeter Avalos free(ip); 117736e94dc5SPeter Avalos free(host); 11789f304aafSPeter Avalos if (host_hostkeys != NULL) 11799f304aafSPeter Avalos free_hostkeys(host_hostkeys); 11809f304aafSPeter Avalos if (ip_hostkeys != NULL) 11819f304aafSPeter Avalos free_hostkeys(ip_hostkeys); 118218de8d7fSPeter Avalos return -1; 118318de8d7fSPeter Avalos } 118418de8d7fSPeter Avalos 118518de8d7fSPeter Avalos /* returns 0 if key verifies or -1 if key does NOT verify */ 118618de8d7fSPeter Avalos int 1187*ce74bacaSMatthew Dillon verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key) 118818de8d7fSPeter Avalos { 1189e9778795SPeter Avalos u_int i; 119036e94dc5SPeter Avalos int r = -1, flags = 0; 1191e9778795SPeter Avalos char valid[64], *fp = NULL, *cafp = NULL; 1192e9778795SPeter Avalos struct sshkey *plain = NULL; 11939f304aafSPeter Avalos 1194e9778795SPeter Avalos if ((fp = sshkey_fingerprint(host_key, 1195e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1196e9778795SPeter Avalos error("%s: fingerprint host key: %s", __func__, ssh_err(r)); 1197e9778795SPeter Avalos r = -1; 1198e9778795SPeter Avalos goto out; 1199e9778795SPeter Avalos } 120018de8d7fSPeter Avalos 1201e9778795SPeter Avalos if (sshkey_is_cert(host_key)) { 1202e9778795SPeter Avalos if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, 1203e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1204e9778795SPeter Avalos error("%s: fingerprint CA key: %s", 1205e9778795SPeter Avalos __func__, ssh_err(r)); 1206e9778795SPeter Avalos r = -1; 1207e9778795SPeter Avalos goto out; 1208e9778795SPeter Avalos } 1209e9778795SPeter Avalos sshkey_format_cert_validity(host_key->cert, 1210e9778795SPeter Avalos valid, sizeof(valid)); 1211e9778795SPeter Avalos debug("Server host certificate: %s %s, serial %llu " 1212e9778795SPeter Avalos "ID \"%s\" CA %s %s valid %s", 1213e9778795SPeter Avalos sshkey_ssh_name(host_key), fp, 1214e9778795SPeter Avalos (unsigned long long)host_key->cert->serial, 1215e9778795SPeter Avalos host_key->cert->key_id, 1216e9778795SPeter Avalos sshkey_ssh_name(host_key->cert->signature_key), cafp, 1217e9778795SPeter Avalos valid); 1218e9778795SPeter Avalos for (i = 0; i < host_key->cert->nprincipals; i++) { 1219e9778795SPeter Avalos debug2("Server host certificate hostname: %s", 1220e9778795SPeter Avalos host_key->cert->principals[i]); 1221e9778795SPeter Avalos } 1222e9778795SPeter Avalos } else { 1223*ce74bacaSMatthew Dillon debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp); 1224e9778795SPeter Avalos } 1225e9778795SPeter Avalos 1226e9778795SPeter Avalos if (sshkey_equal(previous_host_key, host_key)) { 1227e9778795SPeter Avalos debug2("%s: server host key %s %s matches cached key", 1228e9778795SPeter Avalos __func__, sshkey_type(host_key), fp); 1229e9778795SPeter Avalos r = 0; 1230e9778795SPeter Avalos goto out; 1231e9778795SPeter Avalos } 1232e9778795SPeter Avalos 1233e9778795SPeter Avalos /* Check in RevokedHostKeys file if specified */ 1234e9778795SPeter Avalos if (options.revoked_host_keys != NULL) { 1235e9778795SPeter Avalos r = sshkey_check_revoked(host_key, options.revoked_host_keys); 1236e9778795SPeter Avalos switch (r) { 1237e9778795SPeter Avalos case 0: 1238e9778795SPeter Avalos break; /* not revoked */ 1239e9778795SPeter Avalos case SSH_ERR_KEY_REVOKED: 1240e9778795SPeter Avalos error("Host key %s %s revoked by file %s", 1241e9778795SPeter Avalos sshkey_type(host_key), fp, 1242e9778795SPeter Avalos options.revoked_host_keys); 1243e9778795SPeter Avalos r = -1; 1244e9778795SPeter Avalos goto out; 1245e9778795SPeter Avalos default: 1246e9778795SPeter Avalos error("Error checking host key %s %s in " 1247e9778795SPeter Avalos "revoked keys file %s: %s", sshkey_type(host_key), 1248e9778795SPeter Avalos fp, options.revoked_host_keys, ssh_err(r)); 1249e9778795SPeter Avalos r = -1; 1250e9778795SPeter Avalos goto out; 1251e9778795SPeter Avalos } 125236e94dc5SPeter Avalos } 125336e94dc5SPeter Avalos 125436e94dc5SPeter Avalos if (options.verify_host_key_dns) { 125536e94dc5SPeter Avalos /* 125636e94dc5SPeter Avalos * XXX certs are not yet supported for DNS, so downgrade 125736e94dc5SPeter Avalos * them and try the plain key. 125836e94dc5SPeter Avalos */ 1259e9778795SPeter Avalos if ((r = sshkey_from_private(host_key, &plain)) != 0) 1260e9778795SPeter Avalos goto out; 1261e9778795SPeter Avalos if (sshkey_is_cert(plain)) 1262e9778795SPeter Avalos sshkey_drop_cert(plain); 126336e94dc5SPeter Avalos if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) { 126418de8d7fSPeter Avalos if (flags & DNS_VERIFY_FOUND) { 126518de8d7fSPeter Avalos if (options.verify_host_key_dns == 1 && 126618de8d7fSPeter Avalos flags & DNS_VERIFY_MATCH && 126736e94dc5SPeter Avalos flags & DNS_VERIFY_SECURE) { 126836e94dc5SPeter Avalos r = 0; 1269e9778795SPeter Avalos goto out; 127036e94dc5SPeter Avalos } 127118de8d7fSPeter Avalos if (flags & DNS_VERIFY_MATCH) { 127218de8d7fSPeter Avalos matching_host_key_dns = 1; 127318de8d7fSPeter Avalos } else { 127436e94dc5SPeter Avalos warn_changed_key(plain); 127536e94dc5SPeter Avalos error("Update the SSHFP RR in DNS " 127636e94dc5SPeter Avalos "with the new host key to get rid " 127736e94dc5SPeter Avalos "of this message."); 127818de8d7fSPeter Avalos } 127918de8d7fSPeter Avalos } 128018de8d7fSPeter Avalos } 128136e94dc5SPeter Avalos } 128236e94dc5SPeter Avalos r = check_host_key(host, hostaddr, options.port, host_key, RDRW, 12831c188a7fSPeter Avalos options.user_hostfiles, options.num_user_hostfiles, 12841c188a7fSPeter Avalos options.system_hostfiles, options.num_system_hostfiles); 128536e94dc5SPeter Avalos 1286e9778795SPeter Avalos out: 1287e9778795SPeter Avalos sshkey_free(plain); 1288e9778795SPeter Avalos free(fp); 1289e9778795SPeter Avalos free(cafp); 129036e94dc5SPeter Avalos if (r == 0 && host_key != NULL) { 1291*ce74bacaSMatthew Dillon sshkey_free(previous_host_key); 1292*ce74bacaSMatthew Dillon r = sshkey_from_private(host_key, &previous_host_key); 129336e94dc5SPeter Avalos } 129436e94dc5SPeter Avalos 129536e94dc5SPeter Avalos return r; 129618de8d7fSPeter Avalos } 129718de8d7fSPeter Avalos 129818de8d7fSPeter Avalos /* 129918de8d7fSPeter Avalos * Starts a dialog with the server, and authenticates the current user on the 130018de8d7fSPeter Avalos * server. This does not need any extra privileges. The basic connection 130118de8d7fSPeter Avalos * to the server must already have been established before this is called. 130218de8d7fSPeter Avalos * If login fails, this function prints an error and never returns. 130318de8d7fSPeter Avalos * This function does not require super-user privileges. 130418de8d7fSPeter Avalos */ 130518de8d7fSPeter Avalos void 130618de8d7fSPeter Avalos ssh_login(Sensitive *sensitive, const char *orighost, 13079f304aafSPeter Avalos struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) 130818de8d7fSPeter Avalos { 130936e94dc5SPeter Avalos char *host; 131018de8d7fSPeter Avalos char *server_user, *local_user; 131118de8d7fSPeter Avalos 131218de8d7fSPeter Avalos local_user = xstrdup(pw->pw_name); 131318de8d7fSPeter Avalos server_user = options.user ? options.user : local_user; 131418de8d7fSPeter Avalos 131518de8d7fSPeter Avalos /* Convert the user-supplied hostname into all lowercase. */ 131618de8d7fSPeter Avalos host = xstrdup(orighost); 131736e94dc5SPeter Avalos lowercase(host); 131818de8d7fSPeter Avalos 131918de8d7fSPeter Avalos /* Exchange protocol version identification strings with the server. */ 132018de8d7fSPeter Avalos ssh_exchange_identification(timeout_ms); 132118de8d7fSPeter Avalos 132218de8d7fSPeter Avalos /* Put the connection into non-blocking mode. */ 132318de8d7fSPeter Avalos packet_set_nonblocking(); 132418de8d7fSPeter Avalos 132518de8d7fSPeter Avalos /* key exchange */ 132618de8d7fSPeter Avalos /* authenticate user */ 1327e9778795SPeter Avalos debug("Authenticating to %s:%d as '%s'", host, port, server_user); 13289f304aafSPeter Avalos ssh_kex2(host, hostaddr, port); 132918de8d7fSPeter Avalos ssh_userauth2(local_user, server_user, host, sensitive); 133036e94dc5SPeter Avalos free(local_user); 133118de8d7fSPeter Avalos } 133218de8d7fSPeter Avalos 133318de8d7fSPeter Avalos void 133418de8d7fSPeter Avalos ssh_put_password(char *password) 133518de8d7fSPeter Avalos { 133618de8d7fSPeter Avalos int size; 133718de8d7fSPeter Avalos char *padded; 133818de8d7fSPeter Avalos 133918de8d7fSPeter Avalos if (datafellows & SSH_BUG_PASSWORDPAD) { 134018de8d7fSPeter Avalos packet_put_cstring(password); 134118de8d7fSPeter Avalos return; 134218de8d7fSPeter Avalos } 1343*ce74bacaSMatthew Dillon size = ROUNDUP(strlen(password) + 1, 32); 134418de8d7fSPeter Avalos padded = xcalloc(1, size); 134518de8d7fSPeter Avalos strlcpy(padded, password, size); 134618de8d7fSPeter Avalos packet_put_string(padded, size); 134736e94dc5SPeter Avalos explicit_bzero(padded, size); 134836e94dc5SPeter Avalos free(padded); 134918de8d7fSPeter Avalos } 135018de8d7fSPeter Avalos 135118de8d7fSPeter Avalos /* print all known host keys for a given host, but skip keys of given type */ 135218de8d7fSPeter Avalos static int 1353*ce74bacaSMatthew Dillon show_other_keys(struct hostkeys *hostkeys, struct sshkey *key) 135418de8d7fSPeter Avalos { 135536e94dc5SPeter Avalos int type[] = { 135636e94dc5SPeter Avalos KEY_RSA, 135736e94dc5SPeter Avalos KEY_DSA, 135836e94dc5SPeter Avalos KEY_ECDSA, 135936e94dc5SPeter Avalos KEY_ED25519, 136036e94dc5SPeter Avalos -1 136136e94dc5SPeter Avalos }; 13629f304aafSPeter Avalos int i, ret = 0; 13639f304aafSPeter Avalos char *fp, *ra; 13649f304aafSPeter Avalos const struct hostkey_entry *found; 136518de8d7fSPeter Avalos 136618de8d7fSPeter Avalos for (i = 0; type[i] != -1; i++) { 136718de8d7fSPeter Avalos if (type[i] == key->type) 136818de8d7fSPeter Avalos continue; 13699f304aafSPeter Avalos if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) 137018de8d7fSPeter Avalos continue; 1371e9778795SPeter Avalos fp = sshkey_fingerprint(found->key, 1372e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT); 1373e9778795SPeter Avalos ra = sshkey_fingerprint(found->key, 1374e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_RANDOMART); 1375e9778795SPeter Avalos if (fp == NULL || ra == NULL) 1376e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 13779f304aafSPeter Avalos logit("WARNING: %s key found for host %s\n" 13789f304aafSPeter Avalos "in %s:%lu\n" 13799f304aafSPeter Avalos "%s key fingerprint %s.", 13809f304aafSPeter Avalos key_type(found->key), 13819f304aafSPeter Avalos found->host, found->file, found->line, 13829f304aafSPeter Avalos key_type(found->key), fp); 13839f304aafSPeter Avalos if (options.visual_host_key) 13849f304aafSPeter Avalos logit("%s", ra); 138536e94dc5SPeter Avalos free(ra); 138636e94dc5SPeter Avalos free(fp); 13879f304aafSPeter Avalos ret = 1; 138818de8d7fSPeter Avalos } 13899f304aafSPeter Avalos return ret; 139018de8d7fSPeter Avalos } 139118de8d7fSPeter Avalos 139218de8d7fSPeter Avalos static void 1393*ce74bacaSMatthew Dillon warn_changed_key(struct sshkey *host_key) 139418de8d7fSPeter Avalos { 139518de8d7fSPeter Avalos char *fp; 139618de8d7fSPeter Avalos 1397e9778795SPeter Avalos fp = sshkey_fingerprint(host_key, options.fingerprint_hash, 1398e9778795SPeter Avalos SSH_FP_DEFAULT); 1399e9778795SPeter Avalos if (fp == NULL) 1400e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 140118de8d7fSPeter Avalos 140218de8d7fSPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 140318de8d7fSPeter Avalos error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 140418de8d7fSPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 140518de8d7fSPeter Avalos error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 140618de8d7fSPeter Avalos error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 14079f304aafSPeter Avalos error("It is also possible that a host key has just been changed."); 140818de8d7fSPeter Avalos error("The fingerprint for the %s key sent by the remote host is\n%s.", 14099f304aafSPeter Avalos key_type(host_key), fp); 141018de8d7fSPeter Avalos error("Please contact your system administrator."); 141118de8d7fSPeter Avalos 141236e94dc5SPeter Avalos free(fp); 141318de8d7fSPeter Avalos } 141418de8d7fSPeter Avalos 141518de8d7fSPeter Avalos /* 141618de8d7fSPeter Avalos * Execute a local command 141718de8d7fSPeter Avalos */ 141818de8d7fSPeter Avalos int 141918de8d7fSPeter Avalos ssh_local_cmd(const char *args) 142018de8d7fSPeter Avalos { 142118de8d7fSPeter Avalos char *shell; 142218de8d7fSPeter Avalos pid_t pid; 142318de8d7fSPeter Avalos int status; 14249f304aafSPeter Avalos void (*osighand)(int); 142518de8d7fSPeter Avalos 142618de8d7fSPeter Avalos if (!options.permit_local_command || 142718de8d7fSPeter Avalos args == NULL || !*args) 142818de8d7fSPeter Avalos return (1); 142918de8d7fSPeter Avalos 14309f304aafSPeter Avalos if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 143118de8d7fSPeter Avalos shell = _PATH_BSHELL; 143218de8d7fSPeter Avalos 14339f304aafSPeter Avalos osighand = signal(SIGCHLD, SIG_DFL); 143418de8d7fSPeter Avalos pid = fork(); 143518de8d7fSPeter Avalos if (pid == 0) { 14369f304aafSPeter Avalos signal(SIGPIPE, SIG_DFL); 143718de8d7fSPeter Avalos debug3("Executing %s -c \"%s\"", shell, args); 143818de8d7fSPeter Avalos execl(shell, shell, "-c", args, (char *)NULL); 143918de8d7fSPeter Avalos error("Couldn't execute %s -c \"%s\": %s", 144018de8d7fSPeter Avalos shell, args, strerror(errno)); 144118de8d7fSPeter Avalos _exit(1); 144218de8d7fSPeter Avalos } else if (pid == -1) 144318de8d7fSPeter Avalos fatal("fork failed: %.100s", strerror(errno)); 144418de8d7fSPeter Avalos while (waitpid(pid, &status, 0) == -1) 144518de8d7fSPeter Avalos if (errno != EINTR) 144618de8d7fSPeter Avalos fatal("Couldn't wait for child: %s", strerror(errno)); 14479f304aafSPeter Avalos signal(SIGCHLD, osighand); 144818de8d7fSPeter Avalos 144918de8d7fSPeter Avalos if (!WIFEXITED(status)) 145018de8d7fSPeter Avalos return (1); 145118de8d7fSPeter Avalos 145218de8d7fSPeter Avalos return (WEXITSTATUS(status)); 145318de8d7fSPeter Avalos } 1454e9778795SPeter Avalos 1455e9778795SPeter Avalos void 1456*ce74bacaSMatthew Dillon maybe_add_key_to_agent(char *authfile, struct sshkey *private, char *comment, 1457e9778795SPeter Avalos char *passphrase) 1458e9778795SPeter Avalos { 1459e9778795SPeter Avalos int auth_sock = -1, r; 1460e9778795SPeter Avalos 1461e9778795SPeter Avalos if (options.add_keys_to_agent == 0) 1462e9778795SPeter Avalos return; 1463e9778795SPeter Avalos 1464e9778795SPeter Avalos if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { 1465e9778795SPeter Avalos debug3("no authentication agent, not adding key"); 1466e9778795SPeter Avalos return; 1467e9778795SPeter Avalos } 1468e9778795SPeter Avalos 1469e9778795SPeter Avalos if (options.add_keys_to_agent == 2 && 1470e9778795SPeter Avalos !ask_permission("Add key %s (%s) to agent?", authfile, comment)) { 1471e9778795SPeter Avalos debug3("user denied adding this key"); 1472*ce74bacaSMatthew Dillon close(auth_sock); 1473e9778795SPeter Avalos return; 1474e9778795SPeter Avalos } 1475e9778795SPeter Avalos 1476e9778795SPeter Avalos if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, 1477e9778795SPeter Avalos (options.add_keys_to_agent == 3))) == 0) 1478e9778795SPeter Avalos debug("identity added to agent: %s", authfile); 1479e9778795SPeter Avalos else 1480e9778795SPeter Avalos debug("could not add identity to agent: %s (%d)", authfile, r); 1481*ce74bacaSMatthew Dillon close(auth_sock); 1482e9778795SPeter Avalos } 1483