1*664f4763Szrj /* $OpenBSD: sshconnect.c,v 1.314 2019/02/27 19:37:01 markus 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 26*664f4763Szrj #include <net/if.h> 2718de8d7fSPeter Avalos #include <netinet/in.h> 2818de8d7fSPeter Avalos #include <arpa/inet.h> 2918de8d7fSPeter Avalos 3018de8d7fSPeter Avalos #include <ctype.h> 3118de8d7fSPeter Avalos #include <errno.h> 32856ea928SPeter Avalos #include <fcntl.h> 3318de8d7fSPeter Avalos #include <netdb.h> 3418de8d7fSPeter Avalos #ifdef HAVE_PATHS_H 3518de8d7fSPeter Avalos #include <paths.h> 3618de8d7fSPeter Avalos #endif 3718de8d7fSPeter Avalos #include <pwd.h> 38ce74bacaSMatthew Dillon #ifdef HAVE_POLL_H 39ce74bacaSMatthew Dillon #include <poll.h> 40ce74bacaSMatthew Dillon #endif 419f304aafSPeter Avalos #include <signal.h> 4218de8d7fSPeter Avalos #include <stdarg.h> 4318de8d7fSPeter Avalos #include <stdio.h> 4418de8d7fSPeter Avalos #include <stdlib.h> 4518de8d7fSPeter Avalos #include <string.h> 4618de8d7fSPeter Avalos #include <unistd.h> 47*664f4763Szrj #ifdef HAVE_IFADDRS_H 48*664f4763Szrj # include <ifaddrs.h> 49*664f4763Szrj #endif 5018de8d7fSPeter Avalos 5118de8d7fSPeter Avalos #include "xmalloc.h" 5218de8d7fSPeter Avalos #include "hostfile.h" 5318de8d7fSPeter Avalos #include "ssh.h" 54*664f4763Szrj #include "sshbuf.h" 5518de8d7fSPeter Avalos #include "packet.h" 5618de8d7fSPeter Avalos #include "compat.h" 57*664f4763Szrj #include "sshkey.h" 5818de8d7fSPeter Avalos #include "sshconnect.h" 5918de8d7fSPeter Avalos #include "hostfile.h" 6018de8d7fSPeter Avalos #include "log.h" 6136e94dc5SPeter Avalos #include "misc.h" 6218de8d7fSPeter Avalos #include "readconf.h" 6318de8d7fSPeter Avalos #include "atomicio.h" 6418de8d7fSPeter Avalos #include "dns.h" 6536e94dc5SPeter Avalos #include "monitor_fdpass.h" 66856ea928SPeter Avalos #include "ssh2.h" 6718de8d7fSPeter Avalos #include "version.h" 68e9778795SPeter Avalos #include "authfile.h" 69e9778795SPeter Avalos #include "ssherr.h" 70e9778795SPeter Avalos #include "authfd.h" 71*664f4763Szrj #include "kex.h" 7218de8d7fSPeter Avalos 73ce74bacaSMatthew Dillon struct sshkey *previous_host_key = NULL; 7418de8d7fSPeter Avalos 7518de8d7fSPeter Avalos static int matching_host_key_dns = 0; 7618de8d7fSPeter Avalos 779f304aafSPeter Avalos static pid_t proxy_command_pid = 0; 789f304aafSPeter Avalos 7918de8d7fSPeter Avalos /* import */ 80*664f4763Szrj extern int debug_flag; 8118de8d7fSPeter Avalos extern Options options; 8218de8d7fSPeter Avalos extern char *__progname; 8318de8d7fSPeter Avalos 84ce74bacaSMatthew Dillon static int show_other_keys(struct hostkeys *, struct sshkey *); 85ce74bacaSMatthew 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 102*664f4763Szrj static void 103*664f4763Szrj stderr_null(void) 104*664f4763Szrj { 105*664f4763Szrj int devnull; 106*664f4763Szrj 107*664f4763Szrj if ((devnull = open(_PATH_DEVNULL, O_WRONLY)) == -1) { 108*664f4763Szrj error("Can't open %s for stderr redirection: %s", 109*664f4763Szrj _PATH_DEVNULL, strerror(errno)); 110*664f4763Szrj return; 111*664f4763Szrj } 112*664f4763Szrj if (devnull == STDERR_FILENO) 113*664f4763Szrj return; 114*664f4763Szrj if (dup2(devnull, STDERR_FILENO) == -1) 115*664f4763Szrj error("Cannot redirect stderr to %s", _PATH_DEVNULL); 116*664f4763Szrj if (devnull > STDERR_FILENO) 117*664f4763Szrj close(devnull); 118*664f4763Szrj } 119*664f4763Szrj 12036e94dc5SPeter Avalos /* 12136e94dc5SPeter Avalos * Connect to the given ssh server using a proxy command that passes a 12236e94dc5SPeter Avalos * a connected fd back to us. 12336e94dc5SPeter Avalos */ 12436e94dc5SPeter Avalos static int 125ce74bacaSMatthew Dillon ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host, u_short port, 12636e94dc5SPeter Avalos const char *proxy_command) 12736e94dc5SPeter Avalos { 12836e94dc5SPeter Avalos char *command_string; 12936e94dc5SPeter Avalos int sp[2], sock; 13036e94dc5SPeter Avalos pid_t pid; 13136e94dc5SPeter Avalos char *shell; 13236e94dc5SPeter Avalos 13336e94dc5SPeter Avalos if ((shell = getenv("SHELL")) == NULL) 13436e94dc5SPeter Avalos shell = _PATH_BSHELL; 13536e94dc5SPeter Avalos 13636e94dc5SPeter Avalos if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) 13736e94dc5SPeter Avalos fatal("Could not create socketpair to communicate with " 13836e94dc5SPeter Avalos "proxy dialer: %.100s", strerror(errno)); 13936e94dc5SPeter Avalos 14036e94dc5SPeter Avalos command_string = expand_proxy_command(proxy_command, options.user, 14136e94dc5SPeter Avalos host, port); 14236e94dc5SPeter Avalos debug("Executing proxy dialer command: %.500s", command_string); 14336e94dc5SPeter Avalos 14436e94dc5SPeter Avalos /* Fork and execute the proxy command. */ 14536e94dc5SPeter Avalos if ((pid = fork()) == 0) { 14636e94dc5SPeter Avalos char *argv[10]; 14736e94dc5SPeter Avalos 14836e94dc5SPeter Avalos close(sp[1]); 14936e94dc5SPeter Avalos /* Redirect stdin and stdout. */ 15036e94dc5SPeter Avalos if (sp[0] != 0) { 15136e94dc5SPeter Avalos if (dup2(sp[0], 0) < 0) 15236e94dc5SPeter Avalos perror("dup2 stdin"); 15336e94dc5SPeter Avalos } 15436e94dc5SPeter Avalos if (sp[0] != 1) { 15536e94dc5SPeter Avalos if (dup2(sp[0], 1) < 0) 15636e94dc5SPeter Avalos perror("dup2 stdout"); 15736e94dc5SPeter Avalos } 15836e94dc5SPeter Avalos if (sp[0] >= 2) 15936e94dc5SPeter Avalos close(sp[0]); 16036e94dc5SPeter Avalos 16136e94dc5SPeter Avalos /* 162*664f4763Szrj * Stderr is left for non-ControlPersist connections is so 163*664f4763Szrj * error messages may be printed on the user's terminal. 16436e94dc5SPeter Avalos */ 165*664f4763Szrj if (!debug_flag && options.control_path != NULL && 166*664f4763Szrj options.control_persist) 167*664f4763Szrj stderr_null(); 168*664f4763Szrj 16936e94dc5SPeter Avalos argv[0] = shell; 17036e94dc5SPeter Avalos argv[1] = "-c"; 17136e94dc5SPeter Avalos argv[2] = command_string; 17236e94dc5SPeter Avalos argv[3] = NULL; 17336e94dc5SPeter Avalos 17436e94dc5SPeter Avalos /* 17536e94dc5SPeter Avalos * Execute the proxy command. 17636e94dc5SPeter Avalos * Note that we gave up any extra privileges above. 17736e94dc5SPeter Avalos */ 17836e94dc5SPeter Avalos execv(argv[0], argv); 17936e94dc5SPeter Avalos perror(argv[0]); 18036e94dc5SPeter Avalos exit(1); 18136e94dc5SPeter Avalos } 18236e94dc5SPeter Avalos /* Parent. */ 18336e94dc5SPeter Avalos if (pid < 0) 18436e94dc5SPeter Avalos fatal("fork failed: %.100s", strerror(errno)); 18536e94dc5SPeter Avalos close(sp[0]); 18636e94dc5SPeter Avalos free(command_string); 18736e94dc5SPeter Avalos 18836e94dc5SPeter Avalos if ((sock = mm_receive_fd(sp[1])) == -1) 18936e94dc5SPeter Avalos fatal("proxy dialer did not pass back a connection"); 190e9778795SPeter Avalos close(sp[1]); 19136e94dc5SPeter Avalos 19236e94dc5SPeter Avalos while (waitpid(pid, NULL, 0) == -1) 19336e94dc5SPeter Avalos if (errno != EINTR) 19436e94dc5SPeter Avalos fatal("Couldn't wait for child: %s", strerror(errno)); 19536e94dc5SPeter Avalos 19636e94dc5SPeter Avalos /* Set the connection file descriptors. */ 197ce74bacaSMatthew Dillon if (ssh_packet_set_connection(ssh, sock, sock) == NULL) 198ce74bacaSMatthew Dillon return -1; /* ssh_packet_set_connection logs error */ 19936e94dc5SPeter Avalos 20036e94dc5SPeter Avalos return 0; 20136e94dc5SPeter Avalos } 20236e94dc5SPeter Avalos 20318de8d7fSPeter Avalos /* 20418de8d7fSPeter Avalos * Connect to the given ssh server using a proxy command. 20518de8d7fSPeter Avalos */ 20618de8d7fSPeter Avalos static int 207ce74bacaSMatthew Dillon ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port, 208ce74bacaSMatthew Dillon const char *proxy_command) 20918de8d7fSPeter Avalos { 21036e94dc5SPeter Avalos char *command_string; 21118de8d7fSPeter Avalos int pin[2], pout[2]; 21218de8d7fSPeter Avalos pid_t pid; 21336e94dc5SPeter Avalos char *shell; 21418de8d7fSPeter Avalos 2159f304aafSPeter Avalos if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 21618de8d7fSPeter Avalos shell = _PATH_BSHELL; 21718de8d7fSPeter Avalos 21818de8d7fSPeter Avalos /* Create pipes for communicating with the proxy. */ 21918de8d7fSPeter Avalos if (pipe(pin) < 0 || pipe(pout) < 0) 22018de8d7fSPeter Avalos fatal("Could not create pipes to communicate with the proxy: %.100s", 22118de8d7fSPeter Avalos strerror(errno)); 22218de8d7fSPeter Avalos 22336e94dc5SPeter Avalos command_string = expand_proxy_command(proxy_command, options.user, 22436e94dc5SPeter Avalos host, port); 22518de8d7fSPeter Avalos debug("Executing proxy command: %.500s", command_string); 22618de8d7fSPeter Avalos 22718de8d7fSPeter Avalos /* Fork and execute the proxy command. */ 22818de8d7fSPeter Avalos if ((pid = fork()) == 0) { 22918de8d7fSPeter Avalos char *argv[10]; 23018de8d7fSPeter Avalos 23118de8d7fSPeter Avalos /* Redirect stdin and stdout. */ 23218de8d7fSPeter Avalos close(pin[1]); 23318de8d7fSPeter Avalos if (pin[0] != 0) { 23418de8d7fSPeter Avalos if (dup2(pin[0], 0) < 0) 23518de8d7fSPeter Avalos perror("dup2 stdin"); 23618de8d7fSPeter Avalos close(pin[0]); 23718de8d7fSPeter Avalos } 23818de8d7fSPeter Avalos close(pout[0]); 23918de8d7fSPeter Avalos if (dup2(pout[1], 1) < 0) 24018de8d7fSPeter Avalos perror("dup2 stdout"); 24118de8d7fSPeter Avalos /* Cannot be 1 because pin allocated two descriptors. */ 24218de8d7fSPeter Avalos close(pout[1]); 24318de8d7fSPeter Avalos 244*664f4763Szrj /* 245*664f4763Szrj * Stderr is left for non-ControlPersist connections is so 246*664f4763Szrj * error messages may be printed on the user's terminal. 247*664f4763Szrj */ 248*664f4763Szrj if (!debug_flag && options.control_path != NULL && 249*664f4763Szrj options.control_persist) 250*664f4763Szrj stderr_null(); 251*664f4763Szrj 25218de8d7fSPeter Avalos argv[0] = shell; 25318de8d7fSPeter Avalos argv[1] = "-c"; 25418de8d7fSPeter Avalos argv[2] = command_string; 25518de8d7fSPeter Avalos argv[3] = NULL; 25618de8d7fSPeter Avalos 25718de8d7fSPeter Avalos /* Execute the proxy command. Note that we gave up any 25818de8d7fSPeter Avalos extra privileges above. */ 2599f304aafSPeter Avalos signal(SIGPIPE, SIG_DFL); 26018de8d7fSPeter Avalos execv(argv[0], argv); 26118de8d7fSPeter Avalos perror(argv[0]); 26218de8d7fSPeter Avalos exit(1); 26318de8d7fSPeter Avalos } 26418de8d7fSPeter Avalos /* Parent. */ 26518de8d7fSPeter Avalos if (pid < 0) 26618de8d7fSPeter Avalos fatal("fork failed: %.100s", strerror(errno)); 26718de8d7fSPeter Avalos else 26818de8d7fSPeter Avalos proxy_command_pid = pid; /* save pid to clean up later */ 26918de8d7fSPeter Avalos 27018de8d7fSPeter Avalos /* Close child side of the descriptors. */ 27118de8d7fSPeter Avalos close(pin[0]); 27218de8d7fSPeter Avalos close(pout[1]); 27318de8d7fSPeter Avalos 27418de8d7fSPeter Avalos /* Free the command name. */ 27536e94dc5SPeter Avalos free(command_string); 27618de8d7fSPeter Avalos 27718de8d7fSPeter Avalos /* Set the connection file descriptors. */ 278ce74bacaSMatthew Dillon if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL) 279ce74bacaSMatthew Dillon return -1; /* ssh_packet_set_connection logs error */ 28018de8d7fSPeter Avalos 28118de8d7fSPeter Avalos return 0; 28218de8d7fSPeter Avalos } 28318de8d7fSPeter Avalos 2849f304aafSPeter Avalos void 2859f304aafSPeter Avalos ssh_kill_proxy_command(void) 2869f304aafSPeter Avalos { 2879f304aafSPeter Avalos /* 2889f304aafSPeter Avalos * Send SIGHUP to proxy command if used. We don't wait() in 2899f304aafSPeter Avalos * case it hangs and instead rely on init to reap the child 2909f304aafSPeter Avalos */ 2919f304aafSPeter Avalos if (proxy_command_pid > 1) 2929f304aafSPeter Avalos kill(proxy_command_pid, SIGHUP); 2939f304aafSPeter Avalos } 2949f304aafSPeter Avalos 295*664f4763Szrj #ifdef HAVE_IFADDRS_H 29618de8d7fSPeter Avalos /* 297*664f4763Szrj * Search a interface address list (returned from getifaddrs(3)) for an 298*664f4763Szrj * address that matches the desired address family on the specified interface. 299*664f4763Szrj * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure. 30018de8d7fSPeter Avalos */ 30118de8d7fSPeter Avalos static int 302*664f4763Szrj check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs, 303*664f4763Szrj struct sockaddr_storage *resultp, socklen_t *rlenp) 30418de8d7fSPeter Avalos { 305*664f4763Szrj struct sockaddr_in6 *sa6; 306*664f4763Szrj struct sockaddr_in *sa; 307*664f4763Szrj struct in6_addr *v6addr; 308*664f4763Szrj const struct ifaddrs *ifa; 309*664f4763Szrj int allow_local; 310*664f4763Szrj 311*664f4763Szrj /* 312*664f4763Szrj * Prefer addresses that are not loopback or linklocal, but use them 313*664f4763Szrj * if nothing else matches. 314*664f4763Szrj */ 315*664f4763Szrj for (allow_local = 0; allow_local < 2; allow_local++) { 316*664f4763Szrj for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 317*664f4763Szrj if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL || 318*664f4763Szrj (ifa->ifa_flags & IFF_UP) == 0 || 319*664f4763Szrj ifa->ifa_addr->sa_family != af || 320*664f4763Szrj strcmp(ifa->ifa_name, options.bind_interface) != 0) 321*664f4763Szrj continue; 322*664f4763Szrj switch (ifa->ifa_addr->sa_family) { 323*664f4763Szrj case AF_INET: 324*664f4763Szrj sa = (struct sockaddr_in *)ifa->ifa_addr; 325*664f4763Szrj if (!allow_local && sa->sin_addr.s_addr == 326*664f4763Szrj htonl(INADDR_LOOPBACK)) 327*664f4763Szrj continue; 328*664f4763Szrj if (*rlenp < sizeof(struct sockaddr_in)) { 329*664f4763Szrj error("%s: v4 addr doesn't fit", 330*664f4763Szrj __func__); 331*664f4763Szrj return -1; 332*664f4763Szrj } 333*664f4763Szrj *rlenp = sizeof(struct sockaddr_in); 334*664f4763Szrj memcpy(resultp, sa, *rlenp); 335*664f4763Szrj return 0; 336*664f4763Szrj case AF_INET6: 337*664f4763Szrj sa6 = (struct sockaddr_in6 *)ifa->ifa_addr; 338*664f4763Szrj v6addr = &sa6->sin6_addr; 339*664f4763Szrj if (!allow_local && 340*664f4763Szrj (IN6_IS_ADDR_LINKLOCAL(v6addr) || 341*664f4763Szrj IN6_IS_ADDR_LOOPBACK(v6addr))) 342*664f4763Szrj continue; 343*664f4763Szrj if (*rlenp < sizeof(struct sockaddr_in6)) { 344*664f4763Szrj error("%s: v6 addr doesn't fit", 345*664f4763Szrj __func__); 346*664f4763Szrj return -1; 347*664f4763Szrj } 348*664f4763Szrj *rlenp = sizeof(struct sockaddr_in6); 349*664f4763Szrj memcpy(resultp, sa6, *rlenp); 350*664f4763Szrj return 0; 351*664f4763Szrj } 352*664f4763Szrj } 353*664f4763Szrj } 354*664f4763Szrj return -1; 355*664f4763Szrj } 356*664f4763Szrj #endif 357*664f4763Szrj 358*664f4763Szrj /* 359*664f4763Szrj * Creates a socket for use as the ssh connection. 360*664f4763Szrj */ 361*664f4763Szrj static int 362*664f4763Szrj ssh_create_socket(struct addrinfo *ai) 363*664f4763Szrj { 364*664f4763Szrj int sock, r; 365*664f4763Szrj struct sockaddr_storage bindaddr; 366*664f4763Szrj socklen_t bindaddrlen = 0; 36736e94dc5SPeter Avalos struct addrinfo hints, *res = NULL; 368*664f4763Szrj #ifdef HAVE_IFADDRS_H 369*664f4763Szrj struct ifaddrs *ifaddrs = NULL; 370*664f4763Szrj #endif 371*664f4763Szrj char ntop[NI_MAXHOST]; 37218de8d7fSPeter Avalos 37318de8d7fSPeter Avalos sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 374856ea928SPeter Avalos if (sock < 0) { 37536e94dc5SPeter Avalos error("socket: %s", strerror(errno)); 376856ea928SPeter Avalos return -1; 377856ea928SPeter Avalos } 378856ea928SPeter Avalos fcntl(sock, F_SETFD, FD_CLOEXEC); 37918de8d7fSPeter Avalos 38018de8d7fSPeter Avalos /* Bind the socket to an alternative local IP address */ 381*664f4763Szrj if (options.bind_address == NULL && options.bind_interface == NULL) 38218de8d7fSPeter Avalos return sock; 38318de8d7fSPeter Avalos 384*664f4763Szrj if (options.bind_address != NULL) { 38518de8d7fSPeter Avalos memset(&hints, 0, sizeof(hints)); 38618de8d7fSPeter Avalos hints.ai_family = ai->ai_family; 38718de8d7fSPeter Avalos hints.ai_socktype = ai->ai_socktype; 38818de8d7fSPeter Avalos hints.ai_protocol = ai->ai_protocol; 38918de8d7fSPeter Avalos hints.ai_flags = AI_PASSIVE; 390*664f4763Szrj if ((r = getaddrinfo(options.bind_address, NULL, 391*664f4763Szrj &hints, &res)) != 0) { 39218de8d7fSPeter Avalos error("getaddrinfo: %s: %s", options.bind_address, 393*664f4763Szrj ssh_gai_strerror(r)); 394*664f4763Szrj goto fail; 39518de8d7fSPeter Avalos } 396*664f4763Szrj if (res == NULL) { 397*664f4763Szrj error("getaddrinfo: no addrs"); 398*664f4763Szrj goto fail; 39936e94dc5SPeter Avalos } 400*664f4763Szrj memcpy(&bindaddr, res->ai_addr, res->ai_addrlen); 401*664f4763Szrj bindaddrlen = res->ai_addrlen; 402*664f4763Szrj } else if (options.bind_interface != NULL) { 403*664f4763Szrj #ifdef HAVE_IFADDRS_H 404*664f4763Szrj if ((r = getifaddrs(&ifaddrs)) != 0) { 405*664f4763Szrj error("getifaddrs: %s: %s", options.bind_interface, 40636e94dc5SPeter Avalos strerror(errno)); 40736e94dc5SPeter Avalos goto fail; 40836e94dc5SPeter Avalos } 409*664f4763Szrj bindaddrlen = sizeof(bindaddr); 410*664f4763Szrj if (check_ifaddrs(options.bind_interface, ai->ai_family, 411*664f4763Szrj ifaddrs, &bindaddr, &bindaddrlen) != 0) { 412*664f4763Szrj logit("getifaddrs: %s: no suitable addresses", 413*664f4763Szrj options.bind_interface); 414*664f4763Szrj goto fail; 415*664f4763Szrj } 416*664f4763Szrj #else 417*664f4763Szrj error("BindInterface not supported on this platform."); 418*664f4763Szrj #endif 419*664f4763Szrj } 420*664f4763Szrj if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen, 421*664f4763Szrj ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) { 422*664f4763Szrj error("%s: getnameinfo failed: %s", __func__, 423*664f4763Szrj ssh_gai_strerror(r)); 424*664f4763Szrj goto fail; 425*664f4763Szrj } 426*664f4763Szrj if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) { 427*664f4763Szrj error("bind %s: %s", ntop, strerror(errno)); 428*664f4763Szrj goto fail; 429*664f4763Szrj } 430*664f4763Szrj debug("%s: bound to %s", __func__, ntop); 431*664f4763Szrj /* success */ 432*664f4763Szrj goto out; 43336e94dc5SPeter Avalos fail: 43418de8d7fSPeter Avalos close(sock); 435*664f4763Szrj sock = -1; 436*664f4763Szrj out: 43736e94dc5SPeter Avalos if (res != NULL) 43818de8d7fSPeter Avalos freeaddrinfo(res); 439*664f4763Szrj #ifdef HAVE_IFADDRS_H 440*664f4763Szrj if (ifaddrs != NULL) 441*664f4763Szrj freeifaddrs(ifaddrs); 442*664f4763Szrj #endif 44318de8d7fSPeter Avalos return sock; 44418de8d7fSPeter Avalos } 44518de8d7fSPeter Avalos 446ce74bacaSMatthew Dillon /* 44718de8d7fSPeter Avalos * Opens a TCP/IP connection to the remote server on the given host. 44818de8d7fSPeter Avalos * The address of the remote host will be returned in hostaddr. 449*664f4763Szrj * If port is 0, the default port will be used. 45018de8d7fSPeter Avalos * Connection_attempts specifies the maximum number of tries (one per 45118de8d7fSPeter Avalos * second). If proxy_command is non-NULL, it specifies the command (with %h 45218de8d7fSPeter Avalos * and %p substituted for host and port, respectively) to use to contact 45318de8d7fSPeter Avalos * the daemon. 45418de8d7fSPeter Avalos */ 45536e94dc5SPeter Avalos static int 456ce74bacaSMatthew Dillon ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop, 45736e94dc5SPeter Avalos struct sockaddr_storage *hostaddr, u_short port, int family, 458*664f4763Szrj int connection_attempts, int *timeout_ms, int want_keepalive) 45918de8d7fSPeter Avalos { 460*664f4763Szrj int on = 1, saved_timeout_ms = *timeout_ms; 461*664f4763Szrj int oerrno, sock = -1, attempt; 46218de8d7fSPeter Avalos char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 46336e94dc5SPeter Avalos struct addrinfo *ai; 46418de8d7fSPeter Avalos 465*664f4763Szrj debug2("%s", __func__); 466e9778795SPeter Avalos memset(ntop, 0, sizeof(ntop)); 467e9778795SPeter Avalos memset(strport, 0, sizeof(strport)); 46818de8d7fSPeter Avalos 46918de8d7fSPeter Avalos for (attempt = 0; attempt < connection_attempts; attempt++) { 47018de8d7fSPeter Avalos if (attempt > 0) { 47118de8d7fSPeter Avalos /* Sleep a moment before retrying. */ 47218de8d7fSPeter Avalos sleep(1); 47318de8d7fSPeter Avalos debug("Trying again..."); 47418de8d7fSPeter Avalos } 47518de8d7fSPeter Avalos /* 47618de8d7fSPeter Avalos * Loop through addresses for this host, and try each one in 47718de8d7fSPeter Avalos * sequence until the connection succeeds. 47818de8d7fSPeter Avalos */ 47918de8d7fSPeter Avalos for (ai = aitop; ai; ai = ai->ai_next) { 48036e94dc5SPeter Avalos if (ai->ai_family != AF_INET && 481*664f4763Szrj ai->ai_family != AF_INET6) { 482*664f4763Szrj errno = EAFNOSUPPORT; 48318de8d7fSPeter Avalos continue; 484*664f4763Szrj } 48518de8d7fSPeter Avalos if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 48618de8d7fSPeter Avalos ntop, sizeof(ntop), strport, sizeof(strport), 48718de8d7fSPeter Avalos NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 488*664f4763Szrj oerrno = errno; 489e9778795SPeter Avalos error("%s: getnameinfo failed", __func__); 490*664f4763Szrj errno = oerrno; 49118de8d7fSPeter Avalos continue; 49218de8d7fSPeter Avalos } 49318de8d7fSPeter Avalos debug("Connecting to %.200s [%.100s] port %s.", 49418de8d7fSPeter Avalos host, ntop, strport); 49518de8d7fSPeter Avalos 49618de8d7fSPeter Avalos /* Create a socket for connecting. */ 497*664f4763Szrj sock = ssh_create_socket(ai); 498*664f4763Szrj if (sock < 0) { 49918de8d7fSPeter Avalos /* Any error is already output */ 500*664f4763Szrj errno = 0; 50118de8d7fSPeter Avalos continue; 502*664f4763Szrj } 50318de8d7fSPeter Avalos 504*664f4763Szrj *timeout_ms = saved_timeout_ms; 50518de8d7fSPeter Avalos if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 50618de8d7fSPeter Avalos timeout_ms) >= 0) { 50718de8d7fSPeter Avalos /* Successful connection. */ 50818de8d7fSPeter Avalos memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 50918de8d7fSPeter Avalos break; 51018de8d7fSPeter Avalos } else { 511*664f4763Szrj oerrno = errno; 51218de8d7fSPeter Avalos debug("connect to address %s port %s: %s", 51318de8d7fSPeter Avalos ntop, strport, strerror(errno)); 51418de8d7fSPeter Avalos close(sock); 51518de8d7fSPeter Avalos sock = -1; 516*664f4763Szrj errno = oerrno; 51718de8d7fSPeter Avalos } 51818de8d7fSPeter Avalos } 51918de8d7fSPeter Avalos if (sock != -1) 52018de8d7fSPeter Avalos break; /* Successful connection. */ 52118de8d7fSPeter Avalos } 52218de8d7fSPeter Avalos 52318de8d7fSPeter Avalos /* Return failure if we didn't get a successful connection. */ 52418de8d7fSPeter Avalos if (sock == -1) { 52518de8d7fSPeter Avalos error("ssh: connect to host %s port %s: %s", 526*664f4763Szrj host, strport, errno == 0 ? "failure" : strerror(errno)); 527*664f4763Szrj return -1; 52818de8d7fSPeter Avalos } 52918de8d7fSPeter Avalos 53018de8d7fSPeter Avalos debug("Connection established."); 53118de8d7fSPeter Avalos 53218de8d7fSPeter Avalos /* Set SO_KEEPALIVE if requested. */ 53318de8d7fSPeter Avalos if (want_keepalive && 53418de8d7fSPeter Avalos setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 53518de8d7fSPeter Avalos sizeof(on)) < 0) 53618de8d7fSPeter Avalos error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 53718de8d7fSPeter Avalos 53818de8d7fSPeter Avalos /* Set the connection. */ 539ce74bacaSMatthew Dillon if (ssh_packet_set_connection(ssh, sock, sock) == NULL) 540ce74bacaSMatthew Dillon return -1; /* ssh_packet_set_connection logs error */ 54118de8d7fSPeter Avalos 54218de8d7fSPeter Avalos return 0; 54318de8d7fSPeter Avalos } 54418de8d7fSPeter Avalos 54536e94dc5SPeter Avalos int 546ce74bacaSMatthew Dillon ssh_connect(struct ssh *ssh, const char *host, struct addrinfo *addrs, 54736e94dc5SPeter Avalos struct sockaddr_storage *hostaddr, u_short port, int family, 548*664f4763Szrj int connection_attempts, int *timeout_ms, int want_keepalive) 54936e94dc5SPeter Avalos { 550*664f4763Szrj int in, out; 551*664f4763Szrj 55236e94dc5SPeter Avalos if (options.proxy_command == NULL) { 553ce74bacaSMatthew Dillon return ssh_connect_direct(ssh, host, addrs, hostaddr, port, 554*664f4763Szrj family, connection_attempts, timeout_ms, want_keepalive); 55536e94dc5SPeter Avalos } else if (strcmp(options.proxy_command, "-") == 0) { 556*664f4763Szrj if ((in = dup(STDIN_FILENO)) < 0 || 557*664f4763Szrj (out = dup(STDOUT_FILENO)) < 0) { 558*664f4763Szrj if (in >= 0) 559*664f4763Szrj close(in); 560*664f4763Szrj error("%s: dup() in/out failed", __func__); 561*664f4763Szrj return -1; /* ssh_packet_set_connection logs error */ 562*664f4763Szrj } 563*664f4763Szrj if ((ssh_packet_set_connection(ssh, in, out)) == NULL) 564ce74bacaSMatthew Dillon return -1; /* ssh_packet_set_connection logs error */ 565ce74bacaSMatthew Dillon return 0; 56636e94dc5SPeter Avalos } else if (options.proxy_use_fdpass) { 567ce74bacaSMatthew Dillon return ssh_proxy_fdpass_connect(ssh, host, port, 56836e94dc5SPeter Avalos options.proxy_command); 56936e94dc5SPeter Avalos } 570ce74bacaSMatthew Dillon return ssh_proxy_connect(ssh, host, port, options.proxy_command); 57136e94dc5SPeter Avalos } 57236e94dc5SPeter Avalos 57318de8d7fSPeter Avalos /* defaults to 'no' */ 57418de8d7fSPeter Avalos static int 575*664f4763Szrj confirm(const char *prompt, const char *fingerprint) 57618de8d7fSPeter Avalos { 57718de8d7fSPeter Avalos const char *msg, *again = "Please type 'yes' or 'no': "; 578*664f4763Szrj const char *again_fp = "Please type 'yes', 'no' or the fingerprint: "; 57918de8d7fSPeter Avalos char *p; 58018de8d7fSPeter Avalos int ret = -1; 58118de8d7fSPeter Avalos 58218de8d7fSPeter Avalos if (options.batch_mode) 58318de8d7fSPeter Avalos return 0; 584*664f4763Szrj for (msg = prompt;;msg = fingerprint ? again_fp : again) { 58518de8d7fSPeter Avalos p = read_passphrase(msg, RP_ECHO); 586*664f4763Szrj if (p == NULL) 587*664f4763Szrj return 0; 588*664f4763Szrj p[strcspn(p, "\n")] = '\0'; 589*664f4763Szrj if (p[0] == '\0' || strcasecmp(p, "no") == 0) 59018de8d7fSPeter Avalos ret = 0; 591*664f4763Szrj else if (strcasecmp(p, "yes") == 0 || (fingerprint != NULL && 592*664f4763Szrj strcasecmp(p, fingerprint) == 0)) 59318de8d7fSPeter Avalos ret = 1; 59436e94dc5SPeter Avalos free(p); 59518de8d7fSPeter Avalos if (ret != -1) 59618de8d7fSPeter Avalos return ret; 59718de8d7fSPeter Avalos } 59818de8d7fSPeter Avalos } 59918de8d7fSPeter Avalos 600856ea928SPeter Avalos static int 601*664f4763Szrj check_host_cert(const char *host, const struct sshkey *key) 602856ea928SPeter Avalos { 603856ea928SPeter Avalos const char *reason; 604*664f4763Szrj int r; 605856ea928SPeter Avalos 606*664f4763Szrj if (sshkey_cert_check_authority(key, 1, 0, host, &reason) != 0) { 607856ea928SPeter Avalos error("%s", reason); 608856ea928SPeter Avalos return 0; 609856ea928SPeter Avalos } 610*664f4763Szrj if (sshbuf_len(key->cert->critical) != 0) { 611856ea928SPeter Avalos error("Certificate for %s contains unsupported " 612856ea928SPeter Avalos "critical options(s)", host); 613856ea928SPeter Avalos return 0; 614856ea928SPeter Avalos } 615*664f4763Szrj if ((r = sshkey_check_cert_sigtype(key, 616*664f4763Szrj options.ca_sign_algorithms)) != 0) { 617*664f4763Szrj logit("%s: certificate signature algorithm %s: %s", __func__, 618*664f4763Szrj (key->cert == NULL || key->cert->signature_type == NULL) ? 619*664f4763Szrj "(null)" : key->cert->signature_type, ssh_err(r)); 620*664f4763Szrj return 0; 621*664f4763Szrj } 622*664f4763Szrj 623856ea928SPeter Avalos return 1; 624856ea928SPeter Avalos } 625856ea928SPeter Avalos 6269f304aafSPeter Avalos static int 6279f304aafSPeter Avalos sockaddr_is_local(struct sockaddr *hostaddr) 6289f304aafSPeter Avalos { 6299f304aafSPeter Avalos switch (hostaddr->sa_family) { 6309f304aafSPeter Avalos case AF_INET: 6319f304aafSPeter Avalos return (ntohl(((struct sockaddr_in *)hostaddr)-> 6329f304aafSPeter Avalos sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 6339f304aafSPeter Avalos case AF_INET6: 6349f304aafSPeter Avalos return IN6_IS_ADDR_LOOPBACK( 6359f304aafSPeter Avalos &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 6369f304aafSPeter Avalos default: 6379f304aafSPeter Avalos return 0; 6389f304aafSPeter Avalos } 6399f304aafSPeter Avalos } 6409f304aafSPeter Avalos 6419f304aafSPeter Avalos /* 6429f304aafSPeter Avalos * Prepare the hostname and ip address strings that are used to lookup 6439f304aafSPeter Avalos * host keys in known_hosts files. These may have a port number appended. 6449f304aafSPeter Avalos */ 6459f304aafSPeter Avalos void 6469f304aafSPeter Avalos get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, 6479f304aafSPeter Avalos u_short port, char **hostfile_hostname, char **hostfile_ipaddr) 6489f304aafSPeter Avalos { 6499f304aafSPeter Avalos char ntop[NI_MAXHOST]; 6509f304aafSPeter Avalos socklen_t addrlen; 6519f304aafSPeter Avalos 6529f304aafSPeter Avalos switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { 6539f304aafSPeter Avalos case -1: 6549f304aafSPeter Avalos addrlen = 0; 6559f304aafSPeter Avalos break; 6569f304aafSPeter Avalos case AF_INET: 6579f304aafSPeter Avalos addrlen = sizeof(struct sockaddr_in); 6589f304aafSPeter Avalos break; 6599f304aafSPeter Avalos case AF_INET6: 6609f304aafSPeter Avalos addrlen = sizeof(struct sockaddr_in6); 6619f304aafSPeter Avalos break; 6629f304aafSPeter Avalos default: 6639f304aafSPeter Avalos addrlen = sizeof(struct sockaddr); 6649f304aafSPeter Avalos break; 6659f304aafSPeter Avalos } 6669f304aafSPeter Avalos 6679f304aafSPeter Avalos /* 6689f304aafSPeter Avalos * We don't have the remote ip-address for connections 6699f304aafSPeter Avalos * using a proxy command 6709f304aafSPeter Avalos */ 6719f304aafSPeter Avalos if (hostfile_ipaddr != NULL) { 6729f304aafSPeter Avalos if (options.proxy_command == NULL) { 6739f304aafSPeter Avalos if (getnameinfo(hostaddr, addrlen, 6749f304aafSPeter Avalos ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) 675e9778795SPeter Avalos fatal("%s: getnameinfo failed", __func__); 6769f304aafSPeter Avalos *hostfile_ipaddr = put_host_port(ntop, port); 6779f304aafSPeter Avalos } else { 6789f304aafSPeter Avalos *hostfile_ipaddr = xstrdup("<no hostip for proxy " 6799f304aafSPeter Avalos "command>"); 6809f304aafSPeter Avalos } 6819f304aafSPeter Avalos } 6829f304aafSPeter Avalos 6839f304aafSPeter Avalos /* 6849f304aafSPeter Avalos * Allow the user to record the key under a different name or 6859f304aafSPeter Avalos * differentiate a non-standard port. This is useful for ssh 6869f304aafSPeter Avalos * tunneling over forwarded connections or if you run multiple 6879f304aafSPeter Avalos * sshd's on different ports on the same machine. 6889f304aafSPeter Avalos */ 6899f304aafSPeter Avalos if (hostfile_hostname != NULL) { 6909f304aafSPeter Avalos if (options.host_key_alias != NULL) { 6919f304aafSPeter Avalos *hostfile_hostname = xstrdup(options.host_key_alias); 6929f304aafSPeter Avalos debug("using hostkeyalias: %s", *hostfile_hostname); 6939f304aafSPeter Avalos } else { 6949f304aafSPeter Avalos *hostfile_hostname = put_host_port(hostname, port); 6959f304aafSPeter Avalos } 6969f304aafSPeter Avalos } 6979f304aafSPeter Avalos } 6989f304aafSPeter Avalos 69918de8d7fSPeter Avalos /* 70018de8d7fSPeter Avalos * check whether the supplied host key is valid, return -1 if the key 7011c188a7fSPeter Avalos * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. 70218de8d7fSPeter Avalos */ 70318de8d7fSPeter Avalos #define RDRW 0 70418de8d7fSPeter Avalos #define RDONLY 1 70518de8d7fSPeter Avalos #define ROQUIET 2 70618de8d7fSPeter Avalos static int 70718de8d7fSPeter Avalos check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 708ce74bacaSMatthew Dillon struct sshkey *host_key, int readonly, 7091c188a7fSPeter Avalos char **user_hostfiles, u_int num_user_hostfiles, 7101c188a7fSPeter Avalos char **system_hostfiles, u_int num_system_hostfiles) 71118de8d7fSPeter Avalos { 71218de8d7fSPeter Avalos HostStatus host_status; 71318de8d7fSPeter Avalos HostStatus ip_status; 714ce74bacaSMatthew Dillon struct sshkey *raw_key = NULL; 7151c188a7fSPeter Avalos char *ip = NULL, *host = NULL; 7161c188a7fSPeter Avalos char hostline[1000], *hostp, *fp, *ra; 71718de8d7fSPeter Avalos char msg[1024]; 7181c188a7fSPeter Avalos const char *type; 7199f304aafSPeter Avalos const struct hostkey_entry *host_found, *ip_found; 720*664f4763Szrj int len, cancelled_forwarding = 0, confirmed; 7211c188a7fSPeter Avalos int local = sockaddr_is_local(hostaddr); 722ce74bacaSMatthew Dillon int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0; 723e9778795SPeter Avalos int hostkey_trusted = 0; /* Known or explicitly accepted by user */ 7241c188a7fSPeter Avalos struct hostkeys *host_hostkeys, *ip_hostkeys; 7251c188a7fSPeter Avalos u_int i; 72618de8d7fSPeter Avalos 72718de8d7fSPeter Avalos /* 72818de8d7fSPeter Avalos * Force accepting of the host key for loopback/localhost. The 72918de8d7fSPeter Avalos * problem is that if the home directory is NFS-mounted to multiple 73018de8d7fSPeter Avalos * machines, localhost will refer to a different machine in each of 73118de8d7fSPeter Avalos * them, and the user will get bogus HOST_CHANGED warnings. This 73218de8d7fSPeter Avalos * essentially disables host authentication for localhost; however, 73318de8d7fSPeter Avalos * this is probably not a real problem. 73418de8d7fSPeter Avalos */ 73518de8d7fSPeter Avalos if (options.no_host_authentication_for_localhost == 1 && local && 73618de8d7fSPeter Avalos options.host_key_alias == NULL) { 73718de8d7fSPeter Avalos debug("Forcing accepting of host key for " 73818de8d7fSPeter Avalos "loopback/localhost."); 73918de8d7fSPeter Avalos return 0; 74018de8d7fSPeter Avalos } 74118de8d7fSPeter Avalos 74218de8d7fSPeter Avalos /* 7439f304aafSPeter Avalos * Prepare the hostname and address strings used for hostkey lookup. 7449f304aafSPeter Avalos * In some cases, these will have a port number appended. 74518de8d7fSPeter Avalos */ 7469f304aafSPeter Avalos get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); 74718de8d7fSPeter Avalos 74818de8d7fSPeter Avalos /* 74918de8d7fSPeter Avalos * Turn off check_host_ip if the connection is to localhost, via proxy 75018de8d7fSPeter Avalos * command or if we don't have a hostname to compare with 75118de8d7fSPeter Avalos */ 75218de8d7fSPeter Avalos if (options.check_host_ip && (local || 75318de8d7fSPeter Avalos strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 75418de8d7fSPeter Avalos options.check_host_ip = 0; 75518de8d7fSPeter Avalos 7569f304aafSPeter Avalos host_hostkeys = init_hostkeys(); 7571c188a7fSPeter Avalos for (i = 0; i < num_user_hostfiles; i++) 7581c188a7fSPeter Avalos load_hostkeys(host_hostkeys, host, user_hostfiles[i]); 7591c188a7fSPeter Avalos for (i = 0; i < num_system_hostfiles; i++) 7601c188a7fSPeter Avalos load_hostkeys(host_hostkeys, host, system_hostfiles[i]); 7619f304aafSPeter Avalos 7629f304aafSPeter Avalos ip_hostkeys = NULL; 7639f304aafSPeter Avalos if (!want_cert && options.check_host_ip) { 7649f304aafSPeter Avalos ip_hostkeys = init_hostkeys(); 7651c188a7fSPeter Avalos for (i = 0; i < num_user_hostfiles; i++) 7661c188a7fSPeter Avalos load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]); 7671c188a7fSPeter Avalos for (i = 0; i < num_system_hostfiles; i++) 7681c188a7fSPeter Avalos load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]); 76918de8d7fSPeter Avalos } 77018de8d7fSPeter Avalos 771856ea928SPeter Avalos retry: 7729f304aafSPeter Avalos /* Reload these as they may have changed on cert->key downgrade */ 773ce74bacaSMatthew Dillon want_cert = sshkey_is_cert(host_key); 774ce74bacaSMatthew Dillon type = sshkey_type(host_key); 775856ea928SPeter Avalos 77618de8d7fSPeter Avalos /* 77718de8d7fSPeter Avalos * Check if the host key is present in the user's list of known 77818de8d7fSPeter Avalos * hosts or in the systemwide list. 77918de8d7fSPeter Avalos */ 7809f304aafSPeter Avalos host_status = check_key_in_hostkeys(host_hostkeys, host_key, 7819f304aafSPeter Avalos &host_found); 7829f304aafSPeter Avalos 78318de8d7fSPeter Avalos /* 78418de8d7fSPeter Avalos * Also perform check for the ip address, skip the check if we are 785856ea928SPeter Avalos * localhost, looking for a certificate, or the hostname was an ip 786856ea928SPeter Avalos * address to begin with. 78718de8d7fSPeter Avalos */ 7889f304aafSPeter Avalos if (!want_cert && ip_hostkeys != NULL) { 7899f304aafSPeter Avalos ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, 7909f304aafSPeter Avalos &ip_found); 79118de8d7fSPeter Avalos if (host_status == HOST_CHANGED && 7929f304aafSPeter Avalos (ip_status != HOST_CHANGED || 7939f304aafSPeter Avalos (ip_found != NULL && 794ce74bacaSMatthew Dillon !sshkey_equal(ip_found->key, host_found->key)))) 79518de8d7fSPeter Avalos host_ip_differ = 1; 79618de8d7fSPeter Avalos } else 79718de8d7fSPeter Avalos ip_status = host_status; 79818de8d7fSPeter Avalos 79918de8d7fSPeter Avalos switch (host_status) { 80018de8d7fSPeter Avalos case HOST_OK: 80118de8d7fSPeter Avalos /* The host is known and the key matches. */ 802856ea928SPeter Avalos debug("Host '%.200s' is known and matches the %s host %s.", 803856ea928SPeter Avalos host, type, want_cert ? "certificate" : "key"); 8049f304aafSPeter Avalos debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", 8059f304aafSPeter Avalos host_found->file, host_found->line); 806ce74bacaSMatthew Dillon if (want_cert && 807ce74bacaSMatthew Dillon !check_host_cert(options.host_key_alias == NULL ? 808ce74bacaSMatthew Dillon hostname : options.host_key_alias, host_key)) 809856ea928SPeter Avalos goto fail; 81018de8d7fSPeter Avalos if (options.check_host_ip && ip_status == HOST_NEW) { 811856ea928SPeter Avalos if (readonly || want_cert) 81218de8d7fSPeter Avalos logit("%s host key for IP address " 81318de8d7fSPeter Avalos "'%.128s' not in list of known hosts.", 81418de8d7fSPeter Avalos type, ip); 8151c188a7fSPeter Avalos else if (!add_host_to_hostfile(user_hostfiles[0], ip, 81618de8d7fSPeter Avalos host_key, options.hash_known_hosts)) 81718de8d7fSPeter Avalos logit("Failed to add the %s host key for IP " 81818de8d7fSPeter Avalos "address '%.128s' to the list of known " 819e9778795SPeter Avalos "hosts (%.500s).", type, ip, 8201c188a7fSPeter Avalos user_hostfiles[0]); 82118de8d7fSPeter Avalos else 82218de8d7fSPeter Avalos logit("Warning: Permanently added the %s host " 82318de8d7fSPeter Avalos "key for IP address '%.128s' to the list " 82418de8d7fSPeter Avalos "of known hosts.", type, ip); 82518de8d7fSPeter Avalos } else if (options.visual_host_key) { 826e9778795SPeter Avalos fp = sshkey_fingerprint(host_key, 827e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT); 828e9778795SPeter Avalos ra = sshkey_fingerprint(host_key, 829e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_RANDOMART); 830e9778795SPeter Avalos if (fp == NULL || ra == NULL) 831e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 832e9778795SPeter Avalos logit("Host key fingerprint is %s\n%s", fp, ra); 83336e94dc5SPeter Avalos free(ra); 83436e94dc5SPeter Avalos free(fp); 83518de8d7fSPeter Avalos } 836e9778795SPeter Avalos hostkey_trusted = 1; 83718de8d7fSPeter Avalos break; 83818de8d7fSPeter Avalos case HOST_NEW: 83918de8d7fSPeter Avalos if (options.host_key_alias == NULL && port != 0 && 84018de8d7fSPeter Avalos port != SSH_DEFAULT_PORT) { 84118de8d7fSPeter Avalos debug("checking without port identifier"); 842cb5eb4f1SPeter Avalos if (check_host_key(hostname, hostaddr, 0, host_key, 8431c188a7fSPeter Avalos ROQUIET, user_hostfiles, num_user_hostfiles, 8441c188a7fSPeter Avalos system_hostfiles, num_system_hostfiles) == 0) { 84518de8d7fSPeter Avalos debug("found matching key w/out port"); 84618de8d7fSPeter Avalos break; 84718de8d7fSPeter Avalos } 84818de8d7fSPeter Avalos } 849856ea928SPeter Avalos if (readonly || want_cert) 85018de8d7fSPeter Avalos goto fail; 85118de8d7fSPeter Avalos /* The host is new. */ 852ce74bacaSMatthew Dillon if (options.strict_host_key_checking == 853ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_YES) { 85418de8d7fSPeter Avalos /* 85518de8d7fSPeter Avalos * User has requested strict host key checking. We 85618de8d7fSPeter Avalos * will not add the host key automatically. The only 85718de8d7fSPeter Avalos * alternative left is to abort. 85818de8d7fSPeter Avalos */ 85918de8d7fSPeter Avalos error("No %s host key is known for %.200s and you " 86018de8d7fSPeter Avalos "have requested strict checking.", type, host); 86118de8d7fSPeter Avalos goto fail; 862ce74bacaSMatthew Dillon } else if (options.strict_host_key_checking == 863ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_ASK) { 86418de8d7fSPeter Avalos char msg1[1024], msg2[1024]; 86518de8d7fSPeter Avalos 8669f304aafSPeter Avalos if (show_other_keys(host_hostkeys, host_key)) 86718de8d7fSPeter Avalos snprintf(msg1, sizeof(msg1), 86818de8d7fSPeter Avalos "\nbut keys of different type are already" 86918de8d7fSPeter Avalos " known for this host."); 87018de8d7fSPeter Avalos else 87118de8d7fSPeter Avalos snprintf(msg1, sizeof(msg1), "."); 87218de8d7fSPeter Avalos /* The default */ 873e9778795SPeter Avalos fp = sshkey_fingerprint(host_key, 874e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT); 875e9778795SPeter Avalos ra = sshkey_fingerprint(host_key, 876e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_RANDOMART); 877e9778795SPeter Avalos if (fp == NULL || ra == NULL) 878e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 87918de8d7fSPeter Avalos msg2[0] = '\0'; 88018de8d7fSPeter Avalos if (options.verify_host_key_dns) { 88118de8d7fSPeter Avalos if (matching_host_key_dns) 88218de8d7fSPeter Avalos snprintf(msg2, sizeof(msg2), 88318de8d7fSPeter Avalos "Matching host key fingerprint" 88418de8d7fSPeter Avalos " found in DNS.\n"); 88518de8d7fSPeter Avalos else 88618de8d7fSPeter Avalos snprintf(msg2, sizeof(msg2), 88718de8d7fSPeter Avalos "No matching host key fingerprint" 88818de8d7fSPeter Avalos " found in DNS.\n"); 88918de8d7fSPeter Avalos } 89018de8d7fSPeter Avalos snprintf(msg, sizeof(msg), 89118de8d7fSPeter Avalos "The authenticity of host '%.200s (%s)' can't be " 89218de8d7fSPeter Avalos "established%s\n" 89318de8d7fSPeter Avalos "%s key fingerprint is %s.%s%s\n%s" 89418de8d7fSPeter Avalos "Are you sure you want to continue connecting " 895*664f4763Szrj "(yes/no/[fingerprint])? ", 89618de8d7fSPeter Avalos host, ip, msg1, type, fp, 89718de8d7fSPeter Avalos options.visual_host_key ? "\n" : "", 89818de8d7fSPeter Avalos options.visual_host_key ? ra : "", 89918de8d7fSPeter Avalos msg2); 90036e94dc5SPeter Avalos free(ra); 901*664f4763Szrj confirmed = confirm(msg, fp); 90236e94dc5SPeter Avalos free(fp); 903*664f4763Szrj if (!confirmed) 90418de8d7fSPeter Avalos goto fail; 905e9778795SPeter Avalos hostkey_trusted = 1; /* user explicitly confirmed */ 90618de8d7fSPeter Avalos } 90718de8d7fSPeter Avalos /* 908ce74bacaSMatthew Dillon * If in "new" or "off" strict mode, add the key automatically 909ce74bacaSMatthew Dillon * to the local known_hosts file. 91018de8d7fSPeter Avalos */ 91118de8d7fSPeter Avalos if (options.check_host_ip && ip_status == HOST_NEW) { 9129f304aafSPeter Avalos snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 91318de8d7fSPeter Avalos hostp = hostline; 91418de8d7fSPeter Avalos if (options.hash_known_hosts) { 91518de8d7fSPeter Avalos /* Add hash of host and IP separately */ 9161c188a7fSPeter Avalos r = add_host_to_hostfile(user_hostfiles[0], 9171c188a7fSPeter Avalos host, host_key, options.hash_known_hosts) && 9181c188a7fSPeter Avalos add_host_to_hostfile(user_hostfiles[0], ip, 91918de8d7fSPeter Avalos host_key, options.hash_known_hosts); 92018de8d7fSPeter Avalos } else { 92118de8d7fSPeter Avalos /* Add unhashed "host,ip" */ 9221c188a7fSPeter Avalos r = add_host_to_hostfile(user_hostfiles[0], 92318de8d7fSPeter Avalos hostline, host_key, 92418de8d7fSPeter Avalos options.hash_known_hosts); 92518de8d7fSPeter Avalos } 92618de8d7fSPeter Avalos } else { 9271c188a7fSPeter Avalos r = add_host_to_hostfile(user_hostfiles[0], host, 9281c188a7fSPeter Avalos host_key, options.hash_known_hosts); 92918de8d7fSPeter Avalos hostp = host; 93018de8d7fSPeter Avalos } 93118de8d7fSPeter Avalos 93218de8d7fSPeter Avalos if (!r) 93318de8d7fSPeter Avalos logit("Failed to add the host to the list of known " 9341c188a7fSPeter Avalos "hosts (%.500s).", user_hostfiles[0]); 93518de8d7fSPeter Avalos else 93618de8d7fSPeter Avalos logit("Warning: Permanently added '%.200s' (%s) to the " 93718de8d7fSPeter Avalos "list of known hosts.", hostp, type); 93818de8d7fSPeter Avalos break; 939856ea928SPeter Avalos case HOST_REVOKED: 940856ea928SPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 941856ea928SPeter Avalos error("@ WARNING: REVOKED HOST KEY DETECTED! @"); 942856ea928SPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 943856ea928SPeter Avalos error("The %s host key for %s is marked as revoked.", type, host); 944856ea928SPeter Avalos error("This could mean that a stolen key is being used to"); 945856ea928SPeter Avalos error("impersonate this host."); 946856ea928SPeter Avalos 947856ea928SPeter Avalos /* 948856ea928SPeter Avalos * If strict host key checking is in use, the user will have 949856ea928SPeter Avalos * to edit the key manually and we can only abort. 950856ea928SPeter Avalos */ 951ce74bacaSMatthew Dillon if (options.strict_host_key_checking != 952ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_OFF) { 953856ea928SPeter Avalos error("%s host key for %.200s was revoked and you have " 954856ea928SPeter Avalos "requested strict checking.", type, host); 955856ea928SPeter Avalos goto fail; 956856ea928SPeter Avalos } 957856ea928SPeter Avalos goto continue_unsafe; 958856ea928SPeter Avalos 95918de8d7fSPeter Avalos case HOST_CHANGED: 960856ea928SPeter Avalos if (want_cert) { 961856ea928SPeter Avalos /* 962856ea928SPeter Avalos * This is only a debug() since it is valid to have 963856ea928SPeter Avalos * CAs with wildcard DNS matches that don't match 964856ea928SPeter Avalos * all hosts that one might visit. 965856ea928SPeter Avalos */ 966856ea928SPeter Avalos debug("Host certificate authority does not " 9679f304aafSPeter Avalos "match %s in %s:%lu", CA_MARKER, 9689f304aafSPeter Avalos host_found->file, host_found->line); 969856ea928SPeter Avalos goto fail; 970856ea928SPeter Avalos } 97118de8d7fSPeter Avalos if (readonly == ROQUIET) 97218de8d7fSPeter Avalos goto fail; 97318de8d7fSPeter Avalos if (options.check_host_ip && host_ip_differ) { 97418de8d7fSPeter Avalos char *key_msg; 97518de8d7fSPeter Avalos if (ip_status == HOST_NEW) 97618de8d7fSPeter Avalos key_msg = "is unknown"; 97718de8d7fSPeter Avalos else if (ip_status == HOST_OK) 97818de8d7fSPeter Avalos key_msg = "is unchanged"; 97918de8d7fSPeter Avalos else 98018de8d7fSPeter Avalos key_msg = "has a different value"; 98118de8d7fSPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 98218de8d7fSPeter Avalos error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 98318de8d7fSPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 98418de8d7fSPeter Avalos error("The %s host key for %s has changed,", type, host); 98518de8d7fSPeter Avalos error("and the key for the corresponding IP address %s", ip); 98618de8d7fSPeter Avalos error("%s. This could either mean that", key_msg); 98718de8d7fSPeter Avalos error("DNS SPOOFING is happening or the IP address for the host"); 98818de8d7fSPeter Avalos error("and its host key have changed at the same time."); 98918de8d7fSPeter Avalos if (ip_status != HOST_NEW) 9909f304aafSPeter Avalos error("Offending key for IP in %s:%lu", 9919f304aafSPeter Avalos ip_found->file, ip_found->line); 99218de8d7fSPeter Avalos } 99318de8d7fSPeter Avalos /* The host key has changed. */ 99418de8d7fSPeter Avalos warn_changed_key(host_key); 99518de8d7fSPeter Avalos error("Add correct host key in %.100s to get rid of this message.", 9961c188a7fSPeter Avalos user_hostfiles[0]); 997ce74bacaSMatthew Dillon error("Offending %s key in %s:%lu", 998ce74bacaSMatthew Dillon sshkey_type(host_found->key), 9999f304aafSPeter Avalos host_found->file, host_found->line); 100018de8d7fSPeter Avalos 100118de8d7fSPeter Avalos /* 100218de8d7fSPeter Avalos * If strict host key checking is in use, the user will have 100318de8d7fSPeter Avalos * to edit the key manually and we can only abort. 100418de8d7fSPeter Avalos */ 1005ce74bacaSMatthew Dillon if (options.strict_host_key_checking != 1006ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_OFF) { 100718de8d7fSPeter Avalos error("%s host key for %.200s has changed and you have " 100818de8d7fSPeter Avalos "requested strict checking.", type, host); 100918de8d7fSPeter Avalos goto fail; 101018de8d7fSPeter Avalos } 101118de8d7fSPeter Avalos 1012856ea928SPeter Avalos continue_unsafe: 101318de8d7fSPeter Avalos /* 101418de8d7fSPeter Avalos * If strict host key checking has not been requested, allow 101518de8d7fSPeter Avalos * the connection but without MITM-able authentication or 101618de8d7fSPeter Avalos * forwarding. 101718de8d7fSPeter Avalos */ 101818de8d7fSPeter Avalos if (options.password_authentication) { 101918de8d7fSPeter Avalos error("Password authentication is disabled to avoid " 102018de8d7fSPeter Avalos "man-in-the-middle attacks."); 102118de8d7fSPeter Avalos options.password_authentication = 0; 102218de8d7fSPeter Avalos cancelled_forwarding = 1; 102318de8d7fSPeter Avalos } 102418de8d7fSPeter Avalos if (options.kbd_interactive_authentication) { 102518de8d7fSPeter Avalos error("Keyboard-interactive authentication is disabled" 102618de8d7fSPeter Avalos " to avoid man-in-the-middle attacks."); 102718de8d7fSPeter Avalos options.kbd_interactive_authentication = 0; 102818de8d7fSPeter Avalos options.challenge_response_authentication = 0; 102918de8d7fSPeter Avalos cancelled_forwarding = 1; 103018de8d7fSPeter Avalos } 103118de8d7fSPeter Avalos if (options.challenge_response_authentication) { 103218de8d7fSPeter Avalos error("Challenge/response authentication is disabled" 103318de8d7fSPeter Avalos " to avoid man-in-the-middle attacks."); 103418de8d7fSPeter Avalos options.challenge_response_authentication = 0; 103518de8d7fSPeter Avalos cancelled_forwarding = 1; 103618de8d7fSPeter Avalos } 103718de8d7fSPeter Avalos if (options.forward_agent) { 103818de8d7fSPeter Avalos error("Agent forwarding is disabled to avoid " 103918de8d7fSPeter Avalos "man-in-the-middle attacks."); 104018de8d7fSPeter Avalos options.forward_agent = 0; 104118de8d7fSPeter Avalos cancelled_forwarding = 1; 104218de8d7fSPeter Avalos } 104318de8d7fSPeter Avalos if (options.forward_x11) { 104418de8d7fSPeter Avalos error("X11 forwarding is disabled to avoid " 104518de8d7fSPeter Avalos "man-in-the-middle attacks."); 104618de8d7fSPeter Avalos options.forward_x11 = 0; 104718de8d7fSPeter Avalos cancelled_forwarding = 1; 104818de8d7fSPeter Avalos } 104918de8d7fSPeter Avalos if (options.num_local_forwards > 0 || 105018de8d7fSPeter Avalos options.num_remote_forwards > 0) { 105118de8d7fSPeter Avalos error("Port forwarding is disabled to avoid " 105218de8d7fSPeter Avalos "man-in-the-middle attacks."); 105318de8d7fSPeter Avalos options.num_local_forwards = 105418de8d7fSPeter Avalos options.num_remote_forwards = 0; 105518de8d7fSPeter Avalos cancelled_forwarding = 1; 105618de8d7fSPeter Avalos } 105718de8d7fSPeter Avalos if (options.tun_open != SSH_TUNMODE_NO) { 105818de8d7fSPeter Avalos error("Tunnel forwarding is disabled to avoid " 105918de8d7fSPeter Avalos "man-in-the-middle attacks."); 106018de8d7fSPeter Avalos options.tun_open = SSH_TUNMODE_NO; 106118de8d7fSPeter Avalos cancelled_forwarding = 1; 106218de8d7fSPeter Avalos } 106318de8d7fSPeter Avalos if (options.exit_on_forward_failure && cancelled_forwarding) 106418de8d7fSPeter Avalos fatal("Error: forwarding disabled due to host key " 106518de8d7fSPeter Avalos "check failure"); 106618de8d7fSPeter Avalos 106718de8d7fSPeter Avalos /* 106818de8d7fSPeter Avalos * XXX Should permit the user to change to use the new id. 106918de8d7fSPeter Avalos * This could be done by converting the host key to an 107018de8d7fSPeter Avalos * identifying sentence, tell that the host identifies itself 1071856ea928SPeter Avalos * by that sentence, and ask the user if he/she wishes to 107218de8d7fSPeter Avalos * accept the authentication. 107318de8d7fSPeter Avalos */ 107418de8d7fSPeter Avalos break; 107518de8d7fSPeter Avalos case HOST_FOUND: 107618de8d7fSPeter Avalos fatal("internal error"); 107718de8d7fSPeter Avalos break; 107818de8d7fSPeter Avalos } 107918de8d7fSPeter Avalos 108018de8d7fSPeter Avalos if (options.check_host_ip && host_status != HOST_CHANGED && 108118de8d7fSPeter Avalos ip_status == HOST_CHANGED) { 108218de8d7fSPeter Avalos snprintf(msg, sizeof(msg), 108318de8d7fSPeter Avalos "Warning: the %s host key for '%.200s' " 108418de8d7fSPeter Avalos "differs from the key for the IP address '%.128s'" 10859f304aafSPeter Avalos "\nOffending key for IP in %s:%lu", 10869f304aafSPeter Avalos type, host, ip, ip_found->file, ip_found->line); 108718de8d7fSPeter Avalos if (host_status == HOST_OK) { 108818de8d7fSPeter Avalos len = strlen(msg); 108918de8d7fSPeter Avalos snprintf(msg + len, sizeof(msg) - len, 10909f304aafSPeter Avalos "\nMatching host key in %s:%lu", 10919f304aafSPeter Avalos host_found->file, host_found->line); 109218de8d7fSPeter Avalos } 1093ce74bacaSMatthew Dillon if (options.strict_host_key_checking == 1094ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_ASK) { 109518de8d7fSPeter Avalos strlcat(msg, "\nAre you sure you want " 109618de8d7fSPeter Avalos "to continue connecting (yes/no)? ", sizeof(msg)); 1097*664f4763Szrj if (!confirm(msg, NULL)) 109818de8d7fSPeter Avalos goto fail; 1099ce74bacaSMatthew Dillon } else if (options.strict_host_key_checking != 1100ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_OFF) { 1101ce74bacaSMatthew Dillon logit("%s", msg); 1102ce74bacaSMatthew Dillon error("Exiting, you have requested strict checking."); 1103ce74bacaSMatthew Dillon goto fail; 110418de8d7fSPeter Avalos } else { 110518de8d7fSPeter Avalos logit("%s", msg); 110618de8d7fSPeter Avalos } 110718de8d7fSPeter Avalos } 110818de8d7fSPeter Avalos 1109e9778795SPeter Avalos if (!hostkey_trusted && options.update_hostkeys) { 1110e9778795SPeter Avalos debug("%s: hostkey not known or explicitly trusted: " 1111e9778795SPeter Avalos "disabling UpdateHostkeys", __func__); 1112e9778795SPeter Avalos options.update_hostkeys = 0; 1113e9778795SPeter Avalos } 1114e9778795SPeter Avalos 111536e94dc5SPeter Avalos free(ip); 111636e94dc5SPeter Avalos free(host); 11179f304aafSPeter Avalos if (host_hostkeys != NULL) 11189f304aafSPeter Avalos free_hostkeys(host_hostkeys); 11199f304aafSPeter Avalos if (ip_hostkeys != NULL) 11209f304aafSPeter Avalos free_hostkeys(ip_hostkeys); 112118de8d7fSPeter Avalos return 0; 112218de8d7fSPeter Avalos 112318de8d7fSPeter Avalos fail: 1124856ea928SPeter Avalos if (want_cert && host_status != HOST_REVOKED) { 1125856ea928SPeter Avalos /* 1126856ea928SPeter Avalos * No matching certificate. Downgrade cert to raw key and 1127856ea928SPeter Avalos * search normally. 1128856ea928SPeter Avalos */ 1129856ea928SPeter Avalos debug("No matching CA found. Retry with plain key"); 1130ce74bacaSMatthew Dillon if ((r = sshkey_from_private(host_key, &raw_key)) != 0) 1131ce74bacaSMatthew Dillon fatal("%s: sshkey_from_private: %s", 1132ce74bacaSMatthew Dillon __func__, ssh_err(r)); 1133ce74bacaSMatthew Dillon if ((r = sshkey_drop_cert(raw_key)) != 0) 1134ce74bacaSMatthew Dillon fatal("Couldn't drop certificate: %s", ssh_err(r)); 1135856ea928SPeter Avalos host_key = raw_key; 1136856ea928SPeter Avalos goto retry; 1137856ea928SPeter Avalos } 1138ce74bacaSMatthew Dillon sshkey_free(raw_key); 113936e94dc5SPeter Avalos free(ip); 114036e94dc5SPeter Avalos free(host); 11419f304aafSPeter Avalos if (host_hostkeys != NULL) 11429f304aafSPeter Avalos free_hostkeys(host_hostkeys); 11439f304aafSPeter Avalos if (ip_hostkeys != NULL) 11449f304aafSPeter Avalos free_hostkeys(ip_hostkeys); 114518de8d7fSPeter Avalos return -1; 114618de8d7fSPeter Avalos } 114718de8d7fSPeter Avalos 114818de8d7fSPeter Avalos /* returns 0 if key verifies or -1 if key does NOT verify */ 114918de8d7fSPeter Avalos int 1150ce74bacaSMatthew Dillon verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key) 115118de8d7fSPeter Avalos { 1152e9778795SPeter Avalos u_int i; 115336e94dc5SPeter Avalos int r = -1, flags = 0; 1154e9778795SPeter Avalos char valid[64], *fp = NULL, *cafp = NULL; 1155e9778795SPeter Avalos struct sshkey *plain = NULL; 11569f304aafSPeter Avalos 1157e9778795SPeter Avalos if ((fp = sshkey_fingerprint(host_key, 1158e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1159e9778795SPeter Avalos error("%s: fingerprint host key: %s", __func__, ssh_err(r)); 1160e9778795SPeter Avalos r = -1; 1161e9778795SPeter Avalos goto out; 1162e9778795SPeter Avalos } 116318de8d7fSPeter Avalos 1164e9778795SPeter Avalos if (sshkey_is_cert(host_key)) { 1165e9778795SPeter Avalos if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, 1166e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1167e9778795SPeter Avalos error("%s: fingerprint CA key: %s", 1168e9778795SPeter Avalos __func__, ssh_err(r)); 1169e9778795SPeter Avalos r = -1; 1170e9778795SPeter Avalos goto out; 1171e9778795SPeter Avalos } 1172e9778795SPeter Avalos sshkey_format_cert_validity(host_key->cert, 1173e9778795SPeter Avalos valid, sizeof(valid)); 1174e9778795SPeter Avalos debug("Server host certificate: %s %s, serial %llu " 1175e9778795SPeter Avalos "ID \"%s\" CA %s %s valid %s", 1176e9778795SPeter Avalos sshkey_ssh_name(host_key), fp, 1177e9778795SPeter Avalos (unsigned long long)host_key->cert->serial, 1178e9778795SPeter Avalos host_key->cert->key_id, 1179e9778795SPeter Avalos sshkey_ssh_name(host_key->cert->signature_key), cafp, 1180e9778795SPeter Avalos valid); 1181e9778795SPeter Avalos for (i = 0; i < host_key->cert->nprincipals; i++) { 1182e9778795SPeter Avalos debug2("Server host certificate hostname: %s", 1183e9778795SPeter Avalos host_key->cert->principals[i]); 1184e9778795SPeter Avalos } 1185e9778795SPeter Avalos } else { 1186ce74bacaSMatthew Dillon debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp); 1187e9778795SPeter Avalos } 1188e9778795SPeter Avalos 1189e9778795SPeter Avalos if (sshkey_equal(previous_host_key, host_key)) { 1190e9778795SPeter Avalos debug2("%s: server host key %s %s matches cached key", 1191e9778795SPeter Avalos __func__, sshkey_type(host_key), fp); 1192e9778795SPeter Avalos r = 0; 1193e9778795SPeter Avalos goto out; 1194e9778795SPeter Avalos } 1195e9778795SPeter Avalos 1196e9778795SPeter Avalos /* Check in RevokedHostKeys file if specified */ 1197e9778795SPeter Avalos if (options.revoked_host_keys != NULL) { 1198e9778795SPeter Avalos r = sshkey_check_revoked(host_key, options.revoked_host_keys); 1199e9778795SPeter Avalos switch (r) { 1200e9778795SPeter Avalos case 0: 1201e9778795SPeter Avalos break; /* not revoked */ 1202e9778795SPeter Avalos case SSH_ERR_KEY_REVOKED: 1203e9778795SPeter Avalos error("Host key %s %s revoked by file %s", 1204e9778795SPeter Avalos sshkey_type(host_key), fp, 1205e9778795SPeter Avalos options.revoked_host_keys); 1206e9778795SPeter Avalos r = -1; 1207e9778795SPeter Avalos goto out; 1208e9778795SPeter Avalos default: 1209e9778795SPeter Avalos error("Error checking host key %s %s in " 1210e9778795SPeter Avalos "revoked keys file %s: %s", sshkey_type(host_key), 1211e9778795SPeter Avalos fp, options.revoked_host_keys, ssh_err(r)); 1212e9778795SPeter Avalos r = -1; 1213e9778795SPeter Avalos goto out; 1214e9778795SPeter Avalos } 121536e94dc5SPeter Avalos } 121636e94dc5SPeter Avalos 121736e94dc5SPeter Avalos if (options.verify_host_key_dns) { 121836e94dc5SPeter Avalos /* 121936e94dc5SPeter Avalos * XXX certs are not yet supported for DNS, so downgrade 122036e94dc5SPeter Avalos * them and try the plain key. 122136e94dc5SPeter Avalos */ 1222e9778795SPeter Avalos if ((r = sshkey_from_private(host_key, &plain)) != 0) 1223e9778795SPeter Avalos goto out; 1224e9778795SPeter Avalos if (sshkey_is_cert(plain)) 1225e9778795SPeter Avalos sshkey_drop_cert(plain); 122636e94dc5SPeter Avalos if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) { 122718de8d7fSPeter Avalos if (flags & DNS_VERIFY_FOUND) { 122818de8d7fSPeter Avalos if (options.verify_host_key_dns == 1 && 122918de8d7fSPeter Avalos flags & DNS_VERIFY_MATCH && 123036e94dc5SPeter Avalos flags & DNS_VERIFY_SECURE) { 123136e94dc5SPeter Avalos r = 0; 1232e9778795SPeter Avalos goto out; 123336e94dc5SPeter Avalos } 123418de8d7fSPeter Avalos if (flags & DNS_VERIFY_MATCH) { 123518de8d7fSPeter Avalos matching_host_key_dns = 1; 123618de8d7fSPeter Avalos } else { 123736e94dc5SPeter Avalos warn_changed_key(plain); 123836e94dc5SPeter Avalos error("Update the SSHFP RR in DNS " 123936e94dc5SPeter Avalos "with the new host key to get rid " 124036e94dc5SPeter Avalos "of this message."); 124118de8d7fSPeter Avalos } 124218de8d7fSPeter Avalos } 124318de8d7fSPeter Avalos } 124436e94dc5SPeter Avalos } 124536e94dc5SPeter Avalos r = check_host_key(host, hostaddr, options.port, host_key, RDRW, 12461c188a7fSPeter Avalos options.user_hostfiles, options.num_user_hostfiles, 12471c188a7fSPeter Avalos options.system_hostfiles, options.num_system_hostfiles); 124836e94dc5SPeter Avalos 1249e9778795SPeter Avalos out: 1250e9778795SPeter Avalos sshkey_free(plain); 1251e9778795SPeter Avalos free(fp); 1252e9778795SPeter Avalos free(cafp); 125336e94dc5SPeter Avalos if (r == 0 && host_key != NULL) { 1254ce74bacaSMatthew Dillon sshkey_free(previous_host_key); 1255ce74bacaSMatthew Dillon r = sshkey_from_private(host_key, &previous_host_key); 125636e94dc5SPeter Avalos } 125736e94dc5SPeter Avalos 125836e94dc5SPeter Avalos return r; 125918de8d7fSPeter Avalos } 126018de8d7fSPeter Avalos 126118de8d7fSPeter Avalos /* 126218de8d7fSPeter Avalos * Starts a dialog with the server, and authenticates the current user on the 126318de8d7fSPeter Avalos * server. This does not need any extra privileges. The basic connection 126418de8d7fSPeter Avalos * to the server must already have been established before this is called. 126518de8d7fSPeter Avalos * If login fails, this function prints an error and never returns. 126618de8d7fSPeter Avalos * This function does not require super-user privileges. 126718de8d7fSPeter Avalos */ 126818de8d7fSPeter Avalos void 1269*664f4763Szrj ssh_login(struct ssh *ssh, Sensitive *sensitive, const char *orighost, 12709f304aafSPeter Avalos struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) 127118de8d7fSPeter Avalos { 127236e94dc5SPeter Avalos char *host; 127318de8d7fSPeter Avalos char *server_user, *local_user; 127418de8d7fSPeter Avalos 127518de8d7fSPeter Avalos local_user = xstrdup(pw->pw_name); 127618de8d7fSPeter Avalos server_user = options.user ? options.user : local_user; 127718de8d7fSPeter Avalos 127818de8d7fSPeter Avalos /* Convert the user-supplied hostname into all lowercase. */ 127918de8d7fSPeter Avalos host = xstrdup(orighost); 128036e94dc5SPeter Avalos lowercase(host); 128118de8d7fSPeter Avalos 128218de8d7fSPeter Avalos /* Exchange protocol version identification strings with the server. */ 1283*664f4763Szrj if (kex_exchange_identification(ssh, timeout_ms, NULL) != 0) 1284*664f4763Szrj cleanup_exit(255); /* error already logged */ 128518de8d7fSPeter Avalos 128618de8d7fSPeter Avalos /* Put the connection into non-blocking mode. */ 1287*664f4763Szrj ssh_packet_set_nonblocking(ssh); 128818de8d7fSPeter Avalos 128918de8d7fSPeter Avalos /* key exchange */ 129018de8d7fSPeter Avalos /* authenticate user */ 1291e9778795SPeter Avalos debug("Authenticating to %s:%d as '%s'", host, port, server_user); 1292*664f4763Szrj ssh_kex2(ssh, host, hostaddr, port); 1293*664f4763Szrj ssh_userauth2(ssh, local_user, server_user, host, sensitive); 129436e94dc5SPeter Avalos free(local_user); 129518de8d7fSPeter Avalos } 129618de8d7fSPeter Avalos 129718de8d7fSPeter Avalos /* print all known host keys for a given host, but skip keys of given type */ 129818de8d7fSPeter Avalos static int 1299ce74bacaSMatthew Dillon show_other_keys(struct hostkeys *hostkeys, struct sshkey *key) 130018de8d7fSPeter Avalos { 130136e94dc5SPeter Avalos int type[] = { 130236e94dc5SPeter Avalos KEY_RSA, 130336e94dc5SPeter Avalos KEY_DSA, 130436e94dc5SPeter Avalos KEY_ECDSA, 130536e94dc5SPeter Avalos KEY_ED25519, 1306*664f4763Szrj KEY_XMSS, 130736e94dc5SPeter Avalos -1 130836e94dc5SPeter Avalos }; 13099f304aafSPeter Avalos int i, ret = 0; 13109f304aafSPeter Avalos char *fp, *ra; 13119f304aafSPeter Avalos const struct hostkey_entry *found; 131218de8d7fSPeter Avalos 131318de8d7fSPeter Avalos for (i = 0; type[i] != -1; i++) { 131418de8d7fSPeter Avalos if (type[i] == key->type) 131518de8d7fSPeter Avalos continue; 13169f304aafSPeter Avalos if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) 131718de8d7fSPeter Avalos continue; 1318e9778795SPeter Avalos fp = sshkey_fingerprint(found->key, 1319e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT); 1320e9778795SPeter Avalos ra = sshkey_fingerprint(found->key, 1321e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_RANDOMART); 1322e9778795SPeter Avalos if (fp == NULL || ra == NULL) 1323e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 13249f304aafSPeter Avalos logit("WARNING: %s key found for host %s\n" 13259f304aafSPeter Avalos "in %s:%lu\n" 13269f304aafSPeter Avalos "%s key fingerprint %s.", 1327*664f4763Szrj sshkey_type(found->key), 13289f304aafSPeter Avalos found->host, found->file, found->line, 1329*664f4763Szrj sshkey_type(found->key), fp); 13309f304aafSPeter Avalos if (options.visual_host_key) 13319f304aafSPeter Avalos logit("%s", ra); 133236e94dc5SPeter Avalos free(ra); 133336e94dc5SPeter Avalos free(fp); 13349f304aafSPeter Avalos ret = 1; 133518de8d7fSPeter Avalos } 13369f304aafSPeter Avalos return ret; 133718de8d7fSPeter Avalos } 133818de8d7fSPeter Avalos 133918de8d7fSPeter Avalos static void 1340ce74bacaSMatthew Dillon warn_changed_key(struct sshkey *host_key) 134118de8d7fSPeter Avalos { 134218de8d7fSPeter Avalos char *fp; 134318de8d7fSPeter Avalos 1344e9778795SPeter Avalos fp = sshkey_fingerprint(host_key, options.fingerprint_hash, 1345e9778795SPeter Avalos SSH_FP_DEFAULT); 1346e9778795SPeter Avalos if (fp == NULL) 1347e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 134818de8d7fSPeter Avalos 134918de8d7fSPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 135018de8d7fSPeter Avalos error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 135118de8d7fSPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 135218de8d7fSPeter Avalos error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 135318de8d7fSPeter Avalos error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 13549f304aafSPeter Avalos error("It is also possible that a host key has just been changed."); 135518de8d7fSPeter Avalos error("The fingerprint for the %s key sent by the remote host is\n%s.", 1356*664f4763Szrj sshkey_type(host_key), fp); 135718de8d7fSPeter Avalos error("Please contact your system administrator."); 135818de8d7fSPeter Avalos 135936e94dc5SPeter Avalos free(fp); 136018de8d7fSPeter Avalos } 136118de8d7fSPeter Avalos 136218de8d7fSPeter Avalos /* 136318de8d7fSPeter Avalos * Execute a local command 136418de8d7fSPeter Avalos */ 136518de8d7fSPeter Avalos int 136618de8d7fSPeter Avalos ssh_local_cmd(const char *args) 136718de8d7fSPeter Avalos { 136818de8d7fSPeter Avalos char *shell; 136918de8d7fSPeter Avalos pid_t pid; 137018de8d7fSPeter Avalos int status; 13719f304aafSPeter Avalos void (*osighand)(int); 137218de8d7fSPeter Avalos 137318de8d7fSPeter Avalos if (!options.permit_local_command || 137418de8d7fSPeter Avalos args == NULL || !*args) 137518de8d7fSPeter Avalos return (1); 137618de8d7fSPeter Avalos 13779f304aafSPeter Avalos if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 137818de8d7fSPeter Avalos shell = _PATH_BSHELL; 137918de8d7fSPeter Avalos 13809f304aafSPeter Avalos osighand = signal(SIGCHLD, SIG_DFL); 138118de8d7fSPeter Avalos pid = fork(); 138218de8d7fSPeter Avalos if (pid == 0) { 13839f304aafSPeter Avalos signal(SIGPIPE, SIG_DFL); 138418de8d7fSPeter Avalos debug3("Executing %s -c \"%s\"", shell, args); 138518de8d7fSPeter Avalos execl(shell, shell, "-c", args, (char *)NULL); 138618de8d7fSPeter Avalos error("Couldn't execute %s -c \"%s\": %s", 138718de8d7fSPeter Avalos shell, args, strerror(errno)); 138818de8d7fSPeter Avalos _exit(1); 138918de8d7fSPeter Avalos } else if (pid == -1) 139018de8d7fSPeter Avalos fatal("fork failed: %.100s", strerror(errno)); 139118de8d7fSPeter Avalos while (waitpid(pid, &status, 0) == -1) 139218de8d7fSPeter Avalos if (errno != EINTR) 139318de8d7fSPeter Avalos fatal("Couldn't wait for child: %s", strerror(errno)); 13949f304aafSPeter Avalos signal(SIGCHLD, osighand); 139518de8d7fSPeter Avalos 139618de8d7fSPeter Avalos if (!WIFEXITED(status)) 139718de8d7fSPeter Avalos return (1); 139818de8d7fSPeter Avalos 139918de8d7fSPeter Avalos return (WEXITSTATUS(status)); 140018de8d7fSPeter Avalos } 1401e9778795SPeter Avalos 1402e9778795SPeter Avalos void 1403*664f4763Szrj maybe_add_key_to_agent(char *authfile, const struct sshkey *private, 1404*664f4763Szrj char *comment, char *passphrase) 1405e9778795SPeter Avalos { 1406e9778795SPeter Avalos int auth_sock = -1, r; 1407e9778795SPeter Avalos 1408e9778795SPeter Avalos if (options.add_keys_to_agent == 0) 1409e9778795SPeter Avalos return; 1410e9778795SPeter Avalos 1411e9778795SPeter Avalos if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { 1412e9778795SPeter Avalos debug3("no authentication agent, not adding key"); 1413e9778795SPeter Avalos return; 1414e9778795SPeter Avalos } 1415e9778795SPeter Avalos 1416e9778795SPeter Avalos if (options.add_keys_to_agent == 2 && 1417e9778795SPeter Avalos !ask_permission("Add key %s (%s) to agent?", authfile, comment)) { 1418e9778795SPeter Avalos debug3("user denied adding this key"); 1419ce74bacaSMatthew Dillon close(auth_sock); 1420e9778795SPeter Avalos return; 1421e9778795SPeter Avalos } 1422e9778795SPeter Avalos 1423e9778795SPeter Avalos if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, 1424*664f4763Szrj (options.add_keys_to_agent == 3), 0)) == 0) 1425e9778795SPeter Avalos debug("identity added to agent: %s", authfile); 1426e9778795SPeter Avalos else 1427e9778795SPeter Avalos debug("could not add identity to agent: %s (%d)", authfile, r); 1428ce74bacaSMatthew Dillon close(auth_sock); 1429e9778795SPeter Avalos } 1430