1*0cbfa66cSDaniel Fojt /* $OpenBSD: sshconnect.c,v 1.329 2020/03/13 04:01:56 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 26664f4763Szrj #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 <stdio.h> 4318de8d7fSPeter Avalos #include <stdlib.h> 44*0cbfa66cSDaniel Fojt #include <stdarg.h> 4518de8d7fSPeter Avalos #include <string.h> 4618de8d7fSPeter Avalos #include <unistd.h> 47664f4763Szrj #ifdef HAVE_IFADDRS_H 48664f4763Szrj # include <ifaddrs.h> 49664f4763Szrj #endif 5018de8d7fSPeter Avalos 5118de8d7fSPeter Avalos #include "xmalloc.h" 5218de8d7fSPeter Avalos #include "hostfile.h" 5318de8d7fSPeter Avalos #include "ssh.h" 54664f4763Szrj #include "sshbuf.h" 5518de8d7fSPeter Avalos #include "packet.h" 5618de8d7fSPeter Avalos #include "compat.h" 57664f4763Szrj #include "sshkey.h" 5818de8d7fSPeter Avalos #include "sshconnect.h" 5918de8d7fSPeter Avalos #include "log.h" 6036e94dc5SPeter Avalos #include "misc.h" 6118de8d7fSPeter Avalos #include "readconf.h" 6218de8d7fSPeter Avalos #include "atomicio.h" 6318de8d7fSPeter Avalos #include "dns.h" 6436e94dc5SPeter Avalos #include "monitor_fdpass.h" 65856ea928SPeter Avalos #include "ssh2.h" 6618de8d7fSPeter Avalos #include "version.h" 67e9778795SPeter Avalos #include "authfile.h" 68e9778795SPeter Avalos #include "ssherr.h" 69e9778795SPeter Avalos #include "authfd.h" 70664f4763Szrj #include "kex.h" 7118de8d7fSPeter Avalos 72ce74bacaSMatthew 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 */ 79664f4763Szrj extern int debug_flag; 8018de8d7fSPeter Avalos extern Options options; 8118de8d7fSPeter Avalos extern char *__progname; 8218de8d7fSPeter Avalos 83ce74bacaSMatthew Dillon static int show_other_keys(struct hostkeys *, struct sshkey *); 84ce74bacaSMatthew Dillon static void warn_changed_key(struct sshkey *); 8518de8d7fSPeter Avalos 8636e94dc5SPeter Avalos /* Expand a proxy command */ 8736e94dc5SPeter Avalos static char * 8836e94dc5SPeter Avalos expand_proxy_command(const char *proxy_command, const char *user, 89*0cbfa66cSDaniel Fojt const char *host, const char *host_arg, int port) 9036e94dc5SPeter Avalos { 9136e94dc5SPeter Avalos char *tmp, *ret, strport[NI_MAXSERV]; 9236e94dc5SPeter Avalos 9336e94dc5SPeter Avalos snprintf(strport, sizeof strport, "%d", port); 9436e94dc5SPeter Avalos xasprintf(&tmp, "exec %s", proxy_command); 95*0cbfa66cSDaniel Fojt ret = percent_expand(tmp, 96*0cbfa66cSDaniel Fojt "h", host, 97*0cbfa66cSDaniel Fojt "n", host_arg, 98*0cbfa66cSDaniel Fojt "p", strport, 99*0cbfa66cSDaniel Fojt "r", options.user, 100*0cbfa66cSDaniel Fojt (char *)NULL); 10136e94dc5SPeter Avalos free(tmp); 10236e94dc5SPeter Avalos return ret; 10336e94dc5SPeter Avalos } 10436e94dc5SPeter Avalos 105664f4763Szrj static void 106664f4763Szrj stderr_null(void) 107664f4763Szrj { 108664f4763Szrj int devnull; 109664f4763Szrj 110664f4763Szrj if ((devnull = open(_PATH_DEVNULL, O_WRONLY)) == -1) { 111664f4763Szrj error("Can't open %s for stderr redirection: %s", 112664f4763Szrj _PATH_DEVNULL, strerror(errno)); 113664f4763Szrj return; 114664f4763Szrj } 115664f4763Szrj if (devnull == STDERR_FILENO) 116664f4763Szrj return; 117664f4763Szrj if (dup2(devnull, STDERR_FILENO) == -1) 118664f4763Szrj error("Cannot redirect stderr to %s", _PATH_DEVNULL); 119664f4763Szrj if (devnull > STDERR_FILENO) 120664f4763Szrj close(devnull); 121664f4763Szrj } 122664f4763Szrj 12336e94dc5SPeter Avalos /* 12436e94dc5SPeter Avalos * Connect to the given ssh server using a proxy command that passes a 12536e94dc5SPeter Avalos * a connected fd back to us. 12636e94dc5SPeter Avalos */ 12736e94dc5SPeter Avalos static int 128*0cbfa66cSDaniel Fojt ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host, 129*0cbfa66cSDaniel Fojt const char *host_arg, u_short port, const char *proxy_command) 13036e94dc5SPeter Avalos { 13136e94dc5SPeter Avalos char *command_string; 13236e94dc5SPeter Avalos int sp[2], sock; 13336e94dc5SPeter Avalos pid_t pid; 13436e94dc5SPeter Avalos char *shell; 13536e94dc5SPeter Avalos 13636e94dc5SPeter Avalos if ((shell = getenv("SHELL")) == NULL) 13736e94dc5SPeter Avalos shell = _PATH_BSHELL; 13836e94dc5SPeter Avalos 139*0cbfa66cSDaniel Fojt if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == -1) 14036e94dc5SPeter Avalos fatal("Could not create socketpair to communicate with " 14136e94dc5SPeter Avalos "proxy dialer: %.100s", strerror(errno)); 14236e94dc5SPeter Avalos 14336e94dc5SPeter Avalos command_string = expand_proxy_command(proxy_command, options.user, 144*0cbfa66cSDaniel Fojt host, host_arg, port); 14536e94dc5SPeter Avalos debug("Executing proxy dialer command: %.500s", command_string); 14636e94dc5SPeter Avalos 14736e94dc5SPeter Avalos /* Fork and execute the proxy command. */ 14836e94dc5SPeter Avalos if ((pid = fork()) == 0) { 14936e94dc5SPeter Avalos char *argv[10]; 15036e94dc5SPeter Avalos 15136e94dc5SPeter Avalos close(sp[1]); 15236e94dc5SPeter Avalos /* Redirect stdin and stdout. */ 15336e94dc5SPeter Avalos if (sp[0] != 0) { 154*0cbfa66cSDaniel Fojt if (dup2(sp[0], 0) == -1) 15536e94dc5SPeter Avalos perror("dup2 stdin"); 15636e94dc5SPeter Avalos } 15736e94dc5SPeter Avalos if (sp[0] != 1) { 158*0cbfa66cSDaniel Fojt if (dup2(sp[0], 1) == -1) 15936e94dc5SPeter Avalos perror("dup2 stdout"); 16036e94dc5SPeter Avalos } 16136e94dc5SPeter Avalos if (sp[0] >= 2) 16236e94dc5SPeter Avalos close(sp[0]); 16336e94dc5SPeter Avalos 16436e94dc5SPeter Avalos /* 165664f4763Szrj * Stderr is left for non-ControlPersist connections is so 166664f4763Szrj * error messages may be printed on the user's terminal. 16736e94dc5SPeter Avalos */ 168664f4763Szrj if (!debug_flag && options.control_path != NULL && 169664f4763Szrj options.control_persist) 170664f4763Szrj stderr_null(); 171664f4763Szrj 17236e94dc5SPeter Avalos argv[0] = shell; 17336e94dc5SPeter Avalos argv[1] = "-c"; 17436e94dc5SPeter Avalos argv[2] = command_string; 17536e94dc5SPeter Avalos argv[3] = NULL; 17636e94dc5SPeter Avalos 17736e94dc5SPeter Avalos /* 17836e94dc5SPeter Avalos * Execute the proxy command. 17936e94dc5SPeter Avalos * Note that we gave up any extra privileges above. 18036e94dc5SPeter Avalos */ 18136e94dc5SPeter Avalos execv(argv[0], argv); 18236e94dc5SPeter Avalos perror(argv[0]); 18336e94dc5SPeter Avalos exit(1); 18436e94dc5SPeter Avalos } 18536e94dc5SPeter Avalos /* Parent. */ 186*0cbfa66cSDaniel Fojt if (pid == -1) 18736e94dc5SPeter Avalos fatal("fork failed: %.100s", strerror(errno)); 18836e94dc5SPeter Avalos close(sp[0]); 18936e94dc5SPeter Avalos free(command_string); 19036e94dc5SPeter Avalos 19136e94dc5SPeter Avalos if ((sock = mm_receive_fd(sp[1])) == -1) 19236e94dc5SPeter Avalos fatal("proxy dialer did not pass back a connection"); 193e9778795SPeter Avalos close(sp[1]); 19436e94dc5SPeter Avalos 19536e94dc5SPeter Avalos while (waitpid(pid, NULL, 0) == -1) 19636e94dc5SPeter Avalos if (errno != EINTR) 19736e94dc5SPeter Avalos fatal("Couldn't wait for child: %s", strerror(errno)); 19836e94dc5SPeter Avalos 19936e94dc5SPeter Avalos /* Set the connection file descriptors. */ 200ce74bacaSMatthew Dillon if (ssh_packet_set_connection(ssh, sock, sock) == NULL) 201ce74bacaSMatthew Dillon return -1; /* ssh_packet_set_connection logs error */ 20236e94dc5SPeter Avalos 20336e94dc5SPeter Avalos return 0; 20436e94dc5SPeter Avalos } 20536e94dc5SPeter Avalos 20618de8d7fSPeter Avalos /* 20718de8d7fSPeter Avalos * Connect to the given ssh server using a proxy command. 20818de8d7fSPeter Avalos */ 20918de8d7fSPeter Avalos static int 210*0cbfa66cSDaniel Fojt ssh_proxy_connect(struct ssh *ssh, const char *host, const char *host_arg, 211*0cbfa66cSDaniel Fojt u_short port, const char *proxy_command) 21218de8d7fSPeter Avalos { 21336e94dc5SPeter Avalos char *command_string; 21418de8d7fSPeter Avalos int pin[2], pout[2]; 21518de8d7fSPeter Avalos pid_t pid; 21636e94dc5SPeter Avalos char *shell; 21718de8d7fSPeter Avalos 2189f304aafSPeter Avalos if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 21918de8d7fSPeter Avalos shell = _PATH_BSHELL; 22018de8d7fSPeter Avalos 22118de8d7fSPeter Avalos /* Create pipes for communicating with the proxy. */ 222*0cbfa66cSDaniel Fojt if (pipe(pin) == -1 || pipe(pout) == -1) 22318de8d7fSPeter Avalos fatal("Could not create pipes to communicate with the proxy: %.100s", 22418de8d7fSPeter Avalos strerror(errno)); 22518de8d7fSPeter Avalos 22636e94dc5SPeter Avalos command_string = expand_proxy_command(proxy_command, options.user, 227*0cbfa66cSDaniel Fojt host, host_arg, port); 22818de8d7fSPeter Avalos debug("Executing proxy command: %.500s", command_string); 22918de8d7fSPeter Avalos 23018de8d7fSPeter Avalos /* Fork and execute the proxy command. */ 23118de8d7fSPeter Avalos if ((pid = fork()) == 0) { 23218de8d7fSPeter Avalos char *argv[10]; 23318de8d7fSPeter Avalos 23418de8d7fSPeter Avalos /* Redirect stdin and stdout. */ 23518de8d7fSPeter Avalos close(pin[1]); 23618de8d7fSPeter Avalos if (pin[0] != 0) { 237*0cbfa66cSDaniel Fojt if (dup2(pin[0], 0) == -1) 23818de8d7fSPeter Avalos perror("dup2 stdin"); 23918de8d7fSPeter Avalos close(pin[0]); 24018de8d7fSPeter Avalos } 24118de8d7fSPeter Avalos close(pout[0]); 242*0cbfa66cSDaniel Fojt if (dup2(pout[1], 1) == -1) 24318de8d7fSPeter Avalos perror("dup2 stdout"); 24418de8d7fSPeter Avalos /* Cannot be 1 because pin allocated two descriptors. */ 24518de8d7fSPeter Avalos close(pout[1]); 24618de8d7fSPeter Avalos 247664f4763Szrj /* 248664f4763Szrj * Stderr is left for non-ControlPersist connections is so 249664f4763Szrj * error messages may be printed on the user's terminal. 250664f4763Szrj */ 251664f4763Szrj if (!debug_flag && options.control_path != NULL && 252664f4763Szrj options.control_persist) 253664f4763Szrj stderr_null(); 254664f4763Szrj 25518de8d7fSPeter Avalos argv[0] = shell; 25618de8d7fSPeter Avalos argv[1] = "-c"; 25718de8d7fSPeter Avalos argv[2] = command_string; 25818de8d7fSPeter Avalos argv[3] = NULL; 25918de8d7fSPeter Avalos 26018de8d7fSPeter Avalos /* Execute the proxy command. Note that we gave up any 26118de8d7fSPeter Avalos extra privileges above. */ 262*0cbfa66cSDaniel Fojt ssh_signal(SIGPIPE, SIG_DFL); 26318de8d7fSPeter Avalos execv(argv[0], argv); 26418de8d7fSPeter Avalos perror(argv[0]); 26518de8d7fSPeter Avalos exit(1); 26618de8d7fSPeter Avalos } 26718de8d7fSPeter Avalos /* Parent. */ 268*0cbfa66cSDaniel Fojt if (pid == -1) 26918de8d7fSPeter Avalos fatal("fork failed: %.100s", strerror(errno)); 27018de8d7fSPeter Avalos else 27118de8d7fSPeter Avalos proxy_command_pid = pid; /* save pid to clean up later */ 27218de8d7fSPeter Avalos 27318de8d7fSPeter Avalos /* Close child side of the descriptors. */ 27418de8d7fSPeter Avalos close(pin[0]); 27518de8d7fSPeter Avalos close(pout[1]); 27618de8d7fSPeter Avalos 27718de8d7fSPeter Avalos /* Free the command name. */ 27836e94dc5SPeter Avalos free(command_string); 27918de8d7fSPeter Avalos 28018de8d7fSPeter Avalos /* Set the connection file descriptors. */ 281ce74bacaSMatthew Dillon if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL) 282ce74bacaSMatthew Dillon return -1; /* ssh_packet_set_connection logs error */ 28318de8d7fSPeter Avalos 28418de8d7fSPeter Avalos return 0; 28518de8d7fSPeter Avalos } 28618de8d7fSPeter Avalos 2879f304aafSPeter Avalos void 2889f304aafSPeter Avalos ssh_kill_proxy_command(void) 2899f304aafSPeter Avalos { 2909f304aafSPeter Avalos /* 2919f304aafSPeter Avalos * Send SIGHUP to proxy command if used. We don't wait() in 2929f304aafSPeter Avalos * case it hangs and instead rely on init to reap the child 2939f304aafSPeter Avalos */ 2949f304aafSPeter Avalos if (proxy_command_pid > 1) 2959f304aafSPeter Avalos kill(proxy_command_pid, SIGHUP); 2969f304aafSPeter Avalos } 2979f304aafSPeter Avalos 298664f4763Szrj #ifdef HAVE_IFADDRS_H 29918de8d7fSPeter Avalos /* 300664f4763Szrj * Search a interface address list (returned from getifaddrs(3)) for an 301664f4763Szrj * address that matches the desired address family on the specified interface. 302664f4763Szrj * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure. 30318de8d7fSPeter Avalos */ 30418de8d7fSPeter Avalos static int 305664f4763Szrj check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs, 306664f4763Szrj struct sockaddr_storage *resultp, socklen_t *rlenp) 30718de8d7fSPeter Avalos { 308664f4763Szrj struct sockaddr_in6 *sa6; 309664f4763Szrj struct sockaddr_in *sa; 310664f4763Szrj struct in6_addr *v6addr; 311664f4763Szrj const struct ifaddrs *ifa; 312664f4763Szrj int allow_local; 313664f4763Szrj 314664f4763Szrj /* 315664f4763Szrj * Prefer addresses that are not loopback or linklocal, but use them 316664f4763Szrj * if nothing else matches. 317664f4763Szrj */ 318664f4763Szrj for (allow_local = 0; allow_local < 2; allow_local++) { 319664f4763Szrj for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 320664f4763Szrj if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL || 321664f4763Szrj (ifa->ifa_flags & IFF_UP) == 0 || 322664f4763Szrj ifa->ifa_addr->sa_family != af || 323664f4763Szrj strcmp(ifa->ifa_name, options.bind_interface) != 0) 324664f4763Szrj continue; 325664f4763Szrj switch (ifa->ifa_addr->sa_family) { 326664f4763Szrj case AF_INET: 327664f4763Szrj sa = (struct sockaddr_in *)ifa->ifa_addr; 328664f4763Szrj if (!allow_local && sa->sin_addr.s_addr == 329664f4763Szrj htonl(INADDR_LOOPBACK)) 330664f4763Szrj continue; 331664f4763Szrj if (*rlenp < sizeof(struct sockaddr_in)) { 332664f4763Szrj error("%s: v4 addr doesn't fit", 333664f4763Szrj __func__); 334664f4763Szrj return -1; 335664f4763Szrj } 336664f4763Szrj *rlenp = sizeof(struct sockaddr_in); 337664f4763Szrj memcpy(resultp, sa, *rlenp); 338664f4763Szrj return 0; 339664f4763Szrj case AF_INET6: 340664f4763Szrj sa6 = (struct sockaddr_in6 *)ifa->ifa_addr; 341664f4763Szrj v6addr = &sa6->sin6_addr; 342664f4763Szrj if (!allow_local && 343664f4763Szrj (IN6_IS_ADDR_LINKLOCAL(v6addr) || 344664f4763Szrj IN6_IS_ADDR_LOOPBACK(v6addr))) 345664f4763Szrj continue; 346664f4763Szrj if (*rlenp < sizeof(struct sockaddr_in6)) { 347664f4763Szrj error("%s: v6 addr doesn't fit", 348664f4763Szrj __func__); 349664f4763Szrj return -1; 350664f4763Szrj } 351664f4763Szrj *rlenp = sizeof(struct sockaddr_in6); 352664f4763Szrj memcpy(resultp, sa6, *rlenp); 353664f4763Szrj return 0; 354664f4763Szrj } 355664f4763Szrj } 356664f4763Szrj } 357664f4763Szrj return -1; 358664f4763Szrj } 359664f4763Szrj #endif 360664f4763Szrj 361664f4763Szrj /* 362664f4763Szrj * Creates a socket for use as the ssh connection. 363664f4763Szrj */ 364664f4763Szrj static int 365664f4763Szrj ssh_create_socket(struct addrinfo *ai) 366664f4763Szrj { 367664f4763Szrj int sock, r; 368664f4763Szrj struct sockaddr_storage bindaddr; 369664f4763Szrj socklen_t bindaddrlen = 0; 37036e94dc5SPeter Avalos struct addrinfo hints, *res = NULL; 371664f4763Szrj #ifdef HAVE_IFADDRS_H 372664f4763Szrj struct ifaddrs *ifaddrs = NULL; 373664f4763Szrj #endif 374664f4763Szrj char ntop[NI_MAXHOST]; 37518de8d7fSPeter Avalos 37618de8d7fSPeter Avalos sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 377*0cbfa66cSDaniel Fojt if (sock == -1) { 37836e94dc5SPeter Avalos error("socket: %s", strerror(errno)); 379856ea928SPeter Avalos return -1; 380856ea928SPeter Avalos } 381856ea928SPeter Avalos fcntl(sock, F_SETFD, FD_CLOEXEC); 38218de8d7fSPeter Avalos 38318de8d7fSPeter Avalos /* Bind the socket to an alternative local IP address */ 384664f4763Szrj if (options.bind_address == NULL && options.bind_interface == NULL) 38518de8d7fSPeter Avalos return sock; 38618de8d7fSPeter Avalos 387664f4763Szrj if (options.bind_address != NULL) { 38818de8d7fSPeter Avalos memset(&hints, 0, sizeof(hints)); 38918de8d7fSPeter Avalos hints.ai_family = ai->ai_family; 39018de8d7fSPeter Avalos hints.ai_socktype = ai->ai_socktype; 39118de8d7fSPeter Avalos hints.ai_protocol = ai->ai_protocol; 39218de8d7fSPeter Avalos hints.ai_flags = AI_PASSIVE; 393664f4763Szrj if ((r = getaddrinfo(options.bind_address, NULL, 394664f4763Szrj &hints, &res)) != 0) { 39518de8d7fSPeter Avalos error("getaddrinfo: %s: %s", options.bind_address, 396664f4763Szrj ssh_gai_strerror(r)); 397664f4763Szrj goto fail; 39818de8d7fSPeter Avalos } 399664f4763Szrj if (res == NULL) { 400664f4763Szrj error("getaddrinfo: no addrs"); 401664f4763Szrj goto fail; 40236e94dc5SPeter Avalos } 403664f4763Szrj memcpy(&bindaddr, res->ai_addr, res->ai_addrlen); 404664f4763Szrj bindaddrlen = res->ai_addrlen; 405664f4763Szrj } else if (options.bind_interface != NULL) { 406664f4763Szrj #ifdef HAVE_IFADDRS_H 407664f4763Szrj if ((r = getifaddrs(&ifaddrs)) != 0) { 408664f4763Szrj error("getifaddrs: %s: %s", options.bind_interface, 40936e94dc5SPeter Avalos strerror(errno)); 41036e94dc5SPeter Avalos goto fail; 41136e94dc5SPeter Avalos } 412664f4763Szrj bindaddrlen = sizeof(bindaddr); 413664f4763Szrj if (check_ifaddrs(options.bind_interface, ai->ai_family, 414664f4763Szrj ifaddrs, &bindaddr, &bindaddrlen) != 0) { 415664f4763Szrj logit("getifaddrs: %s: no suitable addresses", 416664f4763Szrj options.bind_interface); 417664f4763Szrj goto fail; 418664f4763Szrj } 419664f4763Szrj #else 420664f4763Szrj error("BindInterface not supported on this platform."); 421664f4763Szrj #endif 422664f4763Szrj } 423664f4763Szrj if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen, 424664f4763Szrj ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) { 425664f4763Szrj error("%s: getnameinfo failed: %s", __func__, 426664f4763Szrj ssh_gai_strerror(r)); 427664f4763Szrj goto fail; 428664f4763Szrj } 429664f4763Szrj if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) { 430664f4763Szrj error("bind %s: %s", ntop, strerror(errno)); 431664f4763Szrj goto fail; 432664f4763Szrj } 433664f4763Szrj debug("%s: bound to %s", __func__, ntop); 434664f4763Szrj /* success */ 435664f4763Szrj goto out; 43636e94dc5SPeter Avalos fail: 43718de8d7fSPeter Avalos close(sock); 438664f4763Szrj sock = -1; 439664f4763Szrj out: 44036e94dc5SPeter Avalos if (res != NULL) 44118de8d7fSPeter Avalos freeaddrinfo(res); 442664f4763Szrj #ifdef HAVE_IFADDRS_H 443664f4763Szrj if (ifaddrs != NULL) 444664f4763Szrj freeifaddrs(ifaddrs); 445664f4763Szrj #endif 44618de8d7fSPeter Avalos return sock; 44718de8d7fSPeter Avalos } 44818de8d7fSPeter Avalos 449ce74bacaSMatthew Dillon /* 45018de8d7fSPeter Avalos * Opens a TCP/IP connection to the remote server on the given host. 45118de8d7fSPeter Avalos * The address of the remote host will be returned in hostaddr. 452664f4763Szrj * If port is 0, the default port will be used. 45318de8d7fSPeter Avalos * Connection_attempts specifies the maximum number of tries (one per 45418de8d7fSPeter Avalos * second). If proxy_command is non-NULL, it specifies the command (with %h 45518de8d7fSPeter Avalos * and %p substituted for host and port, respectively) to use to contact 45618de8d7fSPeter Avalos * the daemon. 45718de8d7fSPeter Avalos */ 45836e94dc5SPeter Avalos static int 459ce74bacaSMatthew Dillon ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop, 46036e94dc5SPeter Avalos struct sockaddr_storage *hostaddr, u_short port, int family, 461664f4763Szrj int connection_attempts, int *timeout_ms, int want_keepalive) 46218de8d7fSPeter Avalos { 463664f4763Szrj int on = 1, saved_timeout_ms = *timeout_ms; 464664f4763Szrj int oerrno, sock = -1, attempt; 46518de8d7fSPeter Avalos char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 46636e94dc5SPeter Avalos struct addrinfo *ai; 46718de8d7fSPeter Avalos 468664f4763Szrj debug2("%s", __func__); 469e9778795SPeter Avalos memset(ntop, 0, sizeof(ntop)); 470e9778795SPeter Avalos memset(strport, 0, sizeof(strport)); 47118de8d7fSPeter Avalos 47218de8d7fSPeter Avalos for (attempt = 0; attempt < connection_attempts; attempt++) { 47318de8d7fSPeter Avalos if (attempt > 0) { 47418de8d7fSPeter Avalos /* Sleep a moment before retrying. */ 47518de8d7fSPeter Avalos sleep(1); 47618de8d7fSPeter Avalos debug("Trying again..."); 47718de8d7fSPeter Avalos } 47818de8d7fSPeter Avalos /* 47918de8d7fSPeter Avalos * Loop through addresses for this host, and try each one in 48018de8d7fSPeter Avalos * sequence until the connection succeeds. 48118de8d7fSPeter Avalos */ 48218de8d7fSPeter Avalos for (ai = aitop; ai; ai = ai->ai_next) { 48336e94dc5SPeter Avalos if (ai->ai_family != AF_INET && 484664f4763Szrj ai->ai_family != AF_INET6) { 485664f4763Szrj errno = EAFNOSUPPORT; 48618de8d7fSPeter Avalos continue; 487664f4763Szrj } 48818de8d7fSPeter Avalos if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 48918de8d7fSPeter Avalos ntop, sizeof(ntop), strport, sizeof(strport), 49018de8d7fSPeter Avalos NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 491664f4763Szrj oerrno = errno; 492e9778795SPeter Avalos error("%s: getnameinfo failed", __func__); 493664f4763Szrj errno = oerrno; 49418de8d7fSPeter Avalos continue; 49518de8d7fSPeter Avalos } 49618de8d7fSPeter Avalos debug("Connecting to %.200s [%.100s] port %s.", 49718de8d7fSPeter Avalos host, ntop, strport); 49818de8d7fSPeter Avalos 49918de8d7fSPeter Avalos /* Create a socket for connecting. */ 500664f4763Szrj sock = ssh_create_socket(ai); 501664f4763Szrj if (sock < 0) { 50218de8d7fSPeter Avalos /* Any error is already output */ 503664f4763Szrj errno = 0; 50418de8d7fSPeter Avalos continue; 505664f4763Szrj } 50618de8d7fSPeter Avalos 507664f4763Szrj *timeout_ms = saved_timeout_ms; 50818de8d7fSPeter Avalos if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 50918de8d7fSPeter Avalos timeout_ms) >= 0) { 51018de8d7fSPeter Avalos /* Successful connection. */ 51118de8d7fSPeter Avalos memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 51218de8d7fSPeter Avalos break; 51318de8d7fSPeter Avalos } else { 514664f4763Szrj oerrno = errno; 51518de8d7fSPeter Avalos debug("connect to address %s port %s: %s", 51618de8d7fSPeter Avalos ntop, strport, strerror(errno)); 51718de8d7fSPeter Avalos close(sock); 51818de8d7fSPeter Avalos sock = -1; 519664f4763Szrj errno = oerrno; 52018de8d7fSPeter Avalos } 52118de8d7fSPeter Avalos } 52218de8d7fSPeter Avalos if (sock != -1) 52318de8d7fSPeter Avalos break; /* Successful connection. */ 52418de8d7fSPeter Avalos } 52518de8d7fSPeter Avalos 52618de8d7fSPeter Avalos /* Return failure if we didn't get a successful connection. */ 52718de8d7fSPeter Avalos if (sock == -1) { 52818de8d7fSPeter Avalos error("ssh: connect to host %s port %s: %s", 529664f4763Szrj host, strport, errno == 0 ? "failure" : strerror(errno)); 530664f4763Szrj return -1; 53118de8d7fSPeter Avalos } 53218de8d7fSPeter Avalos 53318de8d7fSPeter Avalos debug("Connection established."); 53418de8d7fSPeter Avalos 53518de8d7fSPeter Avalos /* Set SO_KEEPALIVE if requested. */ 53618de8d7fSPeter Avalos if (want_keepalive && 53718de8d7fSPeter Avalos setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 538*0cbfa66cSDaniel Fojt sizeof(on)) == -1) 53918de8d7fSPeter Avalos error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 54018de8d7fSPeter Avalos 54118de8d7fSPeter Avalos /* Set the connection. */ 542ce74bacaSMatthew Dillon if (ssh_packet_set_connection(ssh, sock, sock) == NULL) 543ce74bacaSMatthew Dillon return -1; /* ssh_packet_set_connection logs error */ 54418de8d7fSPeter Avalos 54518de8d7fSPeter Avalos return 0; 54618de8d7fSPeter Avalos } 54718de8d7fSPeter Avalos 54836e94dc5SPeter Avalos int 549*0cbfa66cSDaniel Fojt ssh_connect(struct ssh *ssh, const char *host, const char *host_arg, 550*0cbfa66cSDaniel Fojt struct addrinfo *addrs, struct sockaddr_storage *hostaddr, u_short port, 551*0cbfa66cSDaniel Fojt int family, int connection_attempts, int *timeout_ms, int want_keepalive) 55236e94dc5SPeter Avalos { 553664f4763Szrj int in, out; 554664f4763Szrj 55536e94dc5SPeter Avalos if (options.proxy_command == NULL) { 556ce74bacaSMatthew Dillon return ssh_connect_direct(ssh, host, addrs, hostaddr, port, 557664f4763Szrj family, connection_attempts, timeout_ms, want_keepalive); 55836e94dc5SPeter Avalos } else if (strcmp(options.proxy_command, "-") == 0) { 559*0cbfa66cSDaniel Fojt if ((in = dup(STDIN_FILENO)) == -1 || 560*0cbfa66cSDaniel Fojt (out = dup(STDOUT_FILENO)) == -1) { 561664f4763Szrj if (in >= 0) 562664f4763Szrj close(in); 563664f4763Szrj error("%s: dup() in/out failed", __func__); 564664f4763Szrj return -1; /* ssh_packet_set_connection logs error */ 565664f4763Szrj } 566664f4763Szrj if ((ssh_packet_set_connection(ssh, in, out)) == NULL) 567ce74bacaSMatthew Dillon return -1; /* ssh_packet_set_connection logs error */ 568ce74bacaSMatthew Dillon return 0; 56936e94dc5SPeter Avalos } else if (options.proxy_use_fdpass) { 570*0cbfa66cSDaniel Fojt return ssh_proxy_fdpass_connect(ssh, host, host_arg, port, 57136e94dc5SPeter Avalos options.proxy_command); 57236e94dc5SPeter Avalos } 573*0cbfa66cSDaniel Fojt return ssh_proxy_connect(ssh, host, host_arg, port, 574*0cbfa66cSDaniel Fojt options.proxy_command); 57536e94dc5SPeter Avalos } 57636e94dc5SPeter Avalos 57718de8d7fSPeter Avalos /* defaults to 'no' */ 57818de8d7fSPeter Avalos static int 579664f4763Szrj confirm(const char *prompt, const char *fingerprint) 58018de8d7fSPeter Avalos { 58118de8d7fSPeter Avalos const char *msg, *again = "Please type 'yes' or 'no': "; 582664f4763Szrj const char *again_fp = "Please type 'yes', 'no' or the fingerprint: "; 583*0cbfa66cSDaniel Fojt char *p, *cp; 58418de8d7fSPeter Avalos int ret = -1; 58518de8d7fSPeter Avalos 58618de8d7fSPeter Avalos if (options.batch_mode) 58718de8d7fSPeter Avalos return 0; 588664f4763Szrj for (msg = prompt;;msg = fingerprint ? again_fp : again) { 589*0cbfa66cSDaniel Fojt cp = p = read_passphrase(msg, RP_ECHO); 590664f4763Szrj if (p == NULL) 591664f4763Szrj return 0; 592*0cbfa66cSDaniel Fojt p += strspn(p, " \t"); /* skip leading whitespace */ 593*0cbfa66cSDaniel Fojt p[strcspn(p, " \t\n")] = '\0'; /* remove trailing whitespace */ 594664f4763Szrj if (p[0] == '\0' || strcasecmp(p, "no") == 0) 59518de8d7fSPeter Avalos ret = 0; 596664f4763Szrj else if (strcasecmp(p, "yes") == 0 || (fingerprint != NULL && 597664f4763Szrj strcasecmp(p, fingerprint) == 0)) 59818de8d7fSPeter Avalos ret = 1; 599*0cbfa66cSDaniel Fojt free(cp); 60018de8d7fSPeter Avalos if (ret != -1) 60118de8d7fSPeter Avalos return ret; 60218de8d7fSPeter Avalos } 60318de8d7fSPeter Avalos } 60418de8d7fSPeter Avalos 605856ea928SPeter Avalos static int 606664f4763Szrj check_host_cert(const char *host, const struct sshkey *key) 607856ea928SPeter Avalos { 608856ea928SPeter Avalos const char *reason; 609664f4763Szrj int r; 610856ea928SPeter Avalos 611664f4763Szrj if (sshkey_cert_check_authority(key, 1, 0, host, &reason) != 0) { 612856ea928SPeter Avalos error("%s", reason); 613856ea928SPeter Avalos return 0; 614856ea928SPeter Avalos } 615664f4763Szrj if (sshbuf_len(key->cert->critical) != 0) { 616856ea928SPeter Avalos error("Certificate for %s contains unsupported " 617856ea928SPeter Avalos "critical options(s)", host); 618856ea928SPeter Avalos return 0; 619856ea928SPeter Avalos } 620664f4763Szrj if ((r = sshkey_check_cert_sigtype(key, 621664f4763Szrj options.ca_sign_algorithms)) != 0) { 622664f4763Szrj logit("%s: certificate signature algorithm %s: %s", __func__, 623664f4763Szrj (key->cert == NULL || key->cert->signature_type == NULL) ? 624664f4763Szrj "(null)" : key->cert->signature_type, ssh_err(r)); 625664f4763Szrj return 0; 626664f4763Szrj } 627664f4763Szrj 628856ea928SPeter Avalos return 1; 629856ea928SPeter Avalos } 630856ea928SPeter Avalos 6319f304aafSPeter Avalos static int 6329f304aafSPeter Avalos sockaddr_is_local(struct sockaddr *hostaddr) 6339f304aafSPeter Avalos { 6349f304aafSPeter Avalos switch (hostaddr->sa_family) { 6359f304aafSPeter Avalos case AF_INET: 6369f304aafSPeter Avalos return (ntohl(((struct sockaddr_in *)hostaddr)-> 6379f304aafSPeter Avalos sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 6389f304aafSPeter Avalos case AF_INET6: 6399f304aafSPeter Avalos return IN6_IS_ADDR_LOOPBACK( 6409f304aafSPeter Avalos &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 6419f304aafSPeter Avalos default: 6429f304aafSPeter Avalos return 0; 6439f304aafSPeter Avalos } 6449f304aafSPeter Avalos } 6459f304aafSPeter Avalos 6469f304aafSPeter Avalos /* 6479f304aafSPeter Avalos * Prepare the hostname and ip address strings that are used to lookup 6489f304aafSPeter Avalos * host keys in known_hosts files. These may have a port number appended. 6499f304aafSPeter Avalos */ 6509f304aafSPeter Avalos void 6519f304aafSPeter Avalos get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, 6529f304aafSPeter Avalos u_short port, char **hostfile_hostname, char **hostfile_ipaddr) 6539f304aafSPeter Avalos { 6549f304aafSPeter Avalos char ntop[NI_MAXHOST]; 6559f304aafSPeter Avalos socklen_t addrlen; 6569f304aafSPeter Avalos 6579f304aafSPeter Avalos switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { 6589f304aafSPeter Avalos case -1: 6599f304aafSPeter Avalos addrlen = 0; 6609f304aafSPeter Avalos break; 6619f304aafSPeter Avalos case AF_INET: 6629f304aafSPeter Avalos addrlen = sizeof(struct sockaddr_in); 6639f304aafSPeter Avalos break; 6649f304aafSPeter Avalos case AF_INET6: 6659f304aafSPeter Avalos addrlen = sizeof(struct sockaddr_in6); 6669f304aafSPeter Avalos break; 6679f304aafSPeter Avalos default: 6689f304aafSPeter Avalos addrlen = sizeof(struct sockaddr); 6699f304aafSPeter Avalos break; 6709f304aafSPeter Avalos } 6719f304aafSPeter Avalos 6729f304aafSPeter Avalos /* 6739f304aafSPeter Avalos * We don't have the remote ip-address for connections 6749f304aafSPeter Avalos * using a proxy command 6759f304aafSPeter Avalos */ 6769f304aafSPeter Avalos if (hostfile_ipaddr != NULL) { 6779f304aafSPeter Avalos if (options.proxy_command == NULL) { 6789f304aafSPeter Avalos if (getnameinfo(hostaddr, addrlen, 6799f304aafSPeter Avalos ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) 680e9778795SPeter Avalos fatal("%s: getnameinfo failed", __func__); 6819f304aafSPeter Avalos *hostfile_ipaddr = put_host_port(ntop, port); 6829f304aafSPeter Avalos } else { 6839f304aafSPeter Avalos *hostfile_ipaddr = xstrdup("<no hostip for proxy " 6849f304aafSPeter Avalos "command>"); 6859f304aafSPeter Avalos } 6869f304aafSPeter Avalos } 6879f304aafSPeter Avalos 6889f304aafSPeter Avalos /* 6899f304aafSPeter Avalos * Allow the user to record the key under a different name or 6909f304aafSPeter Avalos * differentiate a non-standard port. This is useful for ssh 6919f304aafSPeter Avalos * tunneling over forwarded connections or if you run multiple 6929f304aafSPeter Avalos * sshd's on different ports on the same machine. 6939f304aafSPeter Avalos */ 6949f304aafSPeter Avalos if (hostfile_hostname != NULL) { 6959f304aafSPeter Avalos if (options.host_key_alias != NULL) { 6969f304aafSPeter Avalos *hostfile_hostname = xstrdup(options.host_key_alias); 6979f304aafSPeter Avalos debug("using hostkeyalias: %s", *hostfile_hostname); 6989f304aafSPeter Avalos } else { 6999f304aafSPeter Avalos *hostfile_hostname = put_host_port(hostname, port); 7009f304aafSPeter Avalos } 7019f304aafSPeter Avalos } 7029f304aafSPeter Avalos } 7039f304aafSPeter Avalos 70418de8d7fSPeter Avalos /* 70518de8d7fSPeter Avalos * check whether the supplied host key is valid, return -1 if the key 7061c188a7fSPeter Avalos * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. 70718de8d7fSPeter Avalos */ 70818de8d7fSPeter Avalos #define RDRW 0 70918de8d7fSPeter Avalos #define RDONLY 1 71018de8d7fSPeter Avalos #define ROQUIET 2 71118de8d7fSPeter Avalos static int 71218de8d7fSPeter Avalos check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 713ce74bacaSMatthew Dillon struct sshkey *host_key, int readonly, 7141c188a7fSPeter Avalos char **user_hostfiles, u_int num_user_hostfiles, 7151c188a7fSPeter Avalos char **system_hostfiles, u_int num_system_hostfiles) 71618de8d7fSPeter Avalos { 71718de8d7fSPeter Avalos HostStatus host_status; 71818de8d7fSPeter Avalos HostStatus ip_status; 719ce74bacaSMatthew Dillon struct sshkey *raw_key = NULL; 7201c188a7fSPeter Avalos char *ip = NULL, *host = NULL; 7211c188a7fSPeter Avalos char hostline[1000], *hostp, *fp, *ra; 72218de8d7fSPeter Avalos char msg[1024]; 7231c188a7fSPeter Avalos const char *type; 7249f304aafSPeter Avalos const struct hostkey_entry *host_found, *ip_found; 725664f4763Szrj int len, cancelled_forwarding = 0, confirmed; 7261c188a7fSPeter Avalos int local = sockaddr_is_local(hostaddr); 727ce74bacaSMatthew Dillon int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0; 728e9778795SPeter Avalos int hostkey_trusted = 0; /* Known or explicitly accepted by user */ 7291c188a7fSPeter Avalos struct hostkeys *host_hostkeys, *ip_hostkeys; 7301c188a7fSPeter Avalos u_int i; 73118de8d7fSPeter Avalos 73218de8d7fSPeter Avalos /* 73318de8d7fSPeter Avalos * Force accepting of the host key for loopback/localhost. The 73418de8d7fSPeter Avalos * problem is that if the home directory is NFS-mounted to multiple 73518de8d7fSPeter Avalos * machines, localhost will refer to a different machine in each of 73618de8d7fSPeter Avalos * them, and the user will get bogus HOST_CHANGED warnings. This 73718de8d7fSPeter Avalos * essentially disables host authentication for localhost; however, 73818de8d7fSPeter Avalos * this is probably not a real problem. 73918de8d7fSPeter Avalos */ 74018de8d7fSPeter Avalos if (options.no_host_authentication_for_localhost == 1 && local && 74118de8d7fSPeter Avalos options.host_key_alias == NULL) { 74218de8d7fSPeter Avalos debug("Forcing accepting of host key for " 74318de8d7fSPeter Avalos "loopback/localhost."); 74418de8d7fSPeter Avalos return 0; 74518de8d7fSPeter Avalos } 74618de8d7fSPeter Avalos 74718de8d7fSPeter Avalos /* 7489f304aafSPeter Avalos * Prepare the hostname and address strings used for hostkey lookup. 7499f304aafSPeter Avalos * In some cases, these will have a port number appended. 75018de8d7fSPeter Avalos */ 7519f304aafSPeter Avalos get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); 75218de8d7fSPeter Avalos 75318de8d7fSPeter Avalos /* 75418de8d7fSPeter Avalos * Turn off check_host_ip if the connection is to localhost, via proxy 75518de8d7fSPeter Avalos * command or if we don't have a hostname to compare with 75618de8d7fSPeter Avalos */ 75718de8d7fSPeter Avalos if (options.check_host_ip && (local || 75818de8d7fSPeter Avalos strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 75918de8d7fSPeter Avalos options.check_host_ip = 0; 76018de8d7fSPeter Avalos 7619f304aafSPeter Avalos host_hostkeys = init_hostkeys(); 7621c188a7fSPeter Avalos for (i = 0; i < num_user_hostfiles; i++) 7631c188a7fSPeter Avalos load_hostkeys(host_hostkeys, host, user_hostfiles[i]); 7641c188a7fSPeter Avalos for (i = 0; i < num_system_hostfiles; i++) 7651c188a7fSPeter Avalos load_hostkeys(host_hostkeys, host, system_hostfiles[i]); 7669f304aafSPeter Avalos 7679f304aafSPeter Avalos ip_hostkeys = NULL; 7689f304aafSPeter Avalos if (!want_cert && options.check_host_ip) { 7699f304aafSPeter Avalos ip_hostkeys = init_hostkeys(); 7701c188a7fSPeter Avalos for (i = 0; i < num_user_hostfiles; i++) 7711c188a7fSPeter Avalos load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]); 7721c188a7fSPeter Avalos for (i = 0; i < num_system_hostfiles; i++) 7731c188a7fSPeter Avalos load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]); 77418de8d7fSPeter Avalos } 77518de8d7fSPeter Avalos 776856ea928SPeter Avalos retry: 7779f304aafSPeter Avalos /* Reload these as they may have changed on cert->key downgrade */ 778ce74bacaSMatthew Dillon want_cert = sshkey_is_cert(host_key); 779ce74bacaSMatthew Dillon type = sshkey_type(host_key); 780856ea928SPeter Avalos 78118de8d7fSPeter Avalos /* 78218de8d7fSPeter Avalos * Check if the host key is present in the user's list of known 78318de8d7fSPeter Avalos * hosts or in the systemwide list. 78418de8d7fSPeter Avalos */ 7859f304aafSPeter Avalos host_status = check_key_in_hostkeys(host_hostkeys, host_key, 7869f304aafSPeter Avalos &host_found); 7879f304aafSPeter Avalos 78818de8d7fSPeter Avalos /* 78918de8d7fSPeter Avalos * Also perform check for the ip address, skip the check if we are 790856ea928SPeter Avalos * localhost, looking for a certificate, or the hostname was an ip 791856ea928SPeter Avalos * address to begin with. 79218de8d7fSPeter Avalos */ 7939f304aafSPeter Avalos if (!want_cert && ip_hostkeys != NULL) { 7949f304aafSPeter Avalos ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, 7959f304aafSPeter Avalos &ip_found); 79618de8d7fSPeter Avalos if (host_status == HOST_CHANGED && 7979f304aafSPeter Avalos (ip_status != HOST_CHANGED || 7989f304aafSPeter Avalos (ip_found != NULL && 799ce74bacaSMatthew Dillon !sshkey_equal(ip_found->key, host_found->key)))) 80018de8d7fSPeter Avalos host_ip_differ = 1; 80118de8d7fSPeter Avalos } else 80218de8d7fSPeter Avalos ip_status = host_status; 80318de8d7fSPeter Avalos 80418de8d7fSPeter Avalos switch (host_status) { 80518de8d7fSPeter Avalos case HOST_OK: 80618de8d7fSPeter Avalos /* The host is known and the key matches. */ 807856ea928SPeter Avalos debug("Host '%.200s' is known and matches the %s host %s.", 808856ea928SPeter Avalos host, type, want_cert ? "certificate" : "key"); 8099f304aafSPeter Avalos debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", 8109f304aafSPeter Avalos host_found->file, host_found->line); 811ce74bacaSMatthew Dillon if (want_cert && 812ce74bacaSMatthew Dillon !check_host_cert(options.host_key_alias == NULL ? 813ce74bacaSMatthew Dillon hostname : options.host_key_alias, host_key)) 814856ea928SPeter Avalos goto fail; 81518de8d7fSPeter Avalos if (options.check_host_ip && ip_status == HOST_NEW) { 816856ea928SPeter Avalos if (readonly || want_cert) 81718de8d7fSPeter Avalos logit("%s host key for IP address " 81818de8d7fSPeter Avalos "'%.128s' not in list of known hosts.", 81918de8d7fSPeter Avalos type, ip); 8201c188a7fSPeter Avalos else if (!add_host_to_hostfile(user_hostfiles[0], ip, 82118de8d7fSPeter Avalos host_key, options.hash_known_hosts)) 82218de8d7fSPeter Avalos logit("Failed to add the %s host key for IP " 82318de8d7fSPeter Avalos "address '%.128s' to the list of known " 824e9778795SPeter Avalos "hosts (%.500s).", type, ip, 8251c188a7fSPeter Avalos user_hostfiles[0]); 82618de8d7fSPeter Avalos else 82718de8d7fSPeter Avalos logit("Warning: Permanently added the %s host " 82818de8d7fSPeter Avalos "key for IP address '%.128s' to the list " 82918de8d7fSPeter Avalos "of known hosts.", type, ip); 83018de8d7fSPeter Avalos } else if (options.visual_host_key) { 831e9778795SPeter Avalos fp = sshkey_fingerprint(host_key, 832e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT); 833e9778795SPeter Avalos ra = sshkey_fingerprint(host_key, 834e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_RANDOMART); 835e9778795SPeter Avalos if (fp == NULL || ra == NULL) 836e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 837e9778795SPeter Avalos logit("Host key fingerprint is %s\n%s", fp, ra); 83836e94dc5SPeter Avalos free(ra); 83936e94dc5SPeter Avalos free(fp); 84018de8d7fSPeter Avalos } 841e9778795SPeter Avalos hostkey_trusted = 1; 84218de8d7fSPeter Avalos break; 84318de8d7fSPeter Avalos case HOST_NEW: 84418de8d7fSPeter Avalos if (options.host_key_alias == NULL && port != 0 && 84518de8d7fSPeter Avalos port != SSH_DEFAULT_PORT) { 84618de8d7fSPeter Avalos debug("checking without port identifier"); 847cb5eb4f1SPeter Avalos if (check_host_key(hostname, hostaddr, 0, host_key, 8481c188a7fSPeter Avalos ROQUIET, user_hostfiles, num_user_hostfiles, 8491c188a7fSPeter Avalos system_hostfiles, num_system_hostfiles) == 0) { 85018de8d7fSPeter Avalos debug("found matching key w/out port"); 85118de8d7fSPeter Avalos break; 85218de8d7fSPeter Avalos } 85318de8d7fSPeter Avalos } 854856ea928SPeter Avalos if (readonly || want_cert) 85518de8d7fSPeter Avalos goto fail; 85618de8d7fSPeter Avalos /* The host is new. */ 857ce74bacaSMatthew Dillon if (options.strict_host_key_checking == 858ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_YES) { 85918de8d7fSPeter Avalos /* 86018de8d7fSPeter Avalos * User has requested strict host key checking. We 86118de8d7fSPeter Avalos * will not add the host key automatically. The only 86218de8d7fSPeter Avalos * alternative left is to abort. 86318de8d7fSPeter Avalos */ 86418de8d7fSPeter Avalos error("No %s host key is known for %.200s and you " 86518de8d7fSPeter Avalos "have requested strict checking.", type, host); 86618de8d7fSPeter Avalos goto fail; 867ce74bacaSMatthew Dillon } else if (options.strict_host_key_checking == 868ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_ASK) { 86918de8d7fSPeter Avalos char msg1[1024], msg2[1024]; 87018de8d7fSPeter Avalos 8719f304aafSPeter Avalos if (show_other_keys(host_hostkeys, host_key)) 87218de8d7fSPeter Avalos snprintf(msg1, sizeof(msg1), 87318de8d7fSPeter Avalos "\nbut keys of different type are already" 87418de8d7fSPeter Avalos " known for this host."); 87518de8d7fSPeter Avalos else 87618de8d7fSPeter Avalos snprintf(msg1, sizeof(msg1), "."); 87718de8d7fSPeter Avalos /* The default */ 878e9778795SPeter Avalos fp = sshkey_fingerprint(host_key, 879e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT); 880e9778795SPeter Avalos ra = sshkey_fingerprint(host_key, 881e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_RANDOMART); 882e9778795SPeter Avalos if (fp == NULL || ra == NULL) 883e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 88418de8d7fSPeter Avalos msg2[0] = '\0'; 88518de8d7fSPeter Avalos if (options.verify_host_key_dns) { 88618de8d7fSPeter Avalos if (matching_host_key_dns) 88718de8d7fSPeter Avalos snprintf(msg2, sizeof(msg2), 88818de8d7fSPeter Avalos "Matching host key fingerprint" 88918de8d7fSPeter Avalos " found in DNS.\n"); 89018de8d7fSPeter Avalos else 89118de8d7fSPeter Avalos snprintf(msg2, sizeof(msg2), 89218de8d7fSPeter Avalos "No matching host key fingerprint" 89318de8d7fSPeter Avalos " found in DNS.\n"); 89418de8d7fSPeter Avalos } 89518de8d7fSPeter Avalos snprintf(msg, sizeof(msg), 89618de8d7fSPeter Avalos "The authenticity of host '%.200s (%s)' can't be " 89718de8d7fSPeter Avalos "established%s\n" 89818de8d7fSPeter Avalos "%s key fingerprint is %s.%s%s\n%s" 89918de8d7fSPeter Avalos "Are you sure you want to continue connecting " 900664f4763Szrj "(yes/no/[fingerprint])? ", 90118de8d7fSPeter Avalos host, ip, msg1, type, fp, 90218de8d7fSPeter Avalos options.visual_host_key ? "\n" : "", 90318de8d7fSPeter Avalos options.visual_host_key ? ra : "", 90418de8d7fSPeter Avalos msg2); 90536e94dc5SPeter Avalos free(ra); 906664f4763Szrj confirmed = confirm(msg, fp); 90736e94dc5SPeter Avalos free(fp); 908664f4763Szrj if (!confirmed) 90918de8d7fSPeter Avalos goto fail; 910e9778795SPeter Avalos hostkey_trusted = 1; /* user explicitly confirmed */ 91118de8d7fSPeter Avalos } 91218de8d7fSPeter Avalos /* 913ce74bacaSMatthew Dillon * If in "new" or "off" strict mode, add the key automatically 914ce74bacaSMatthew Dillon * to the local known_hosts file. 91518de8d7fSPeter Avalos */ 91618de8d7fSPeter Avalos if (options.check_host_ip && ip_status == HOST_NEW) { 9179f304aafSPeter Avalos snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 91818de8d7fSPeter Avalos hostp = hostline; 91918de8d7fSPeter Avalos if (options.hash_known_hosts) { 92018de8d7fSPeter Avalos /* Add hash of host and IP separately */ 9211c188a7fSPeter Avalos r = add_host_to_hostfile(user_hostfiles[0], 9221c188a7fSPeter Avalos host, host_key, options.hash_known_hosts) && 9231c188a7fSPeter Avalos add_host_to_hostfile(user_hostfiles[0], ip, 92418de8d7fSPeter Avalos host_key, options.hash_known_hosts); 92518de8d7fSPeter Avalos } else { 92618de8d7fSPeter Avalos /* Add unhashed "host,ip" */ 9271c188a7fSPeter Avalos r = add_host_to_hostfile(user_hostfiles[0], 92818de8d7fSPeter Avalos hostline, host_key, 92918de8d7fSPeter Avalos options.hash_known_hosts); 93018de8d7fSPeter Avalos } 93118de8d7fSPeter Avalos } else { 9321c188a7fSPeter Avalos r = add_host_to_hostfile(user_hostfiles[0], host, 9331c188a7fSPeter Avalos host_key, options.hash_known_hosts); 93418de8d7fSPeter Avalos hostp = host; 93518de8d7fSPeter Avalos } 93618de8d7fSPeter Avalos 93718de8d7fSPeter Avalos if (!r) 93818de8d7fSPeter Avalos logit("Failed to add the host to the list of known " 9391c188a7fSPeter Avalos "hosts (%.500s).", user_hostfiles[0]); 94018de8d7fSPeter Avalos else 94118de8d7fSPeter Avalos logit("Warning: Permanently added '%.200s' (%s) to the " 94218de8d7fSPeter Avalos "list of known hosts.", hostp, type); 94318de8d7fSPeter Avalos break; 944856ea928SPeter Avalos case HOST_REVOKED: 945856ea928SPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 946856ea928SPeter Avalos error("@ WARNING: REVOKED HOST KEY DETECTED! @"); 947856ea928SPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 948856ea928SPeter Avalos error("The %s host key for %s is marked as revoked.", type, host); 949856ea928SPeter Avalos error("This could mean that a stolen key is being used to"); 950856ea928SPeter Avalos error("impersonate this host."); 951856ea928SPeter Avalos 952856ea928SPeter Avalos /* 953856ea928SPeter Avalos * If strict host key checking is in use, the user will have 954856ea928SPeter Avalos * to edit the key manually and we can only abort. 955856ea928SPeter Avalos */ 956ce74bacaSMatthew Dillon if (options.strict_host_key_checking != 957ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_OFF) { 958856ea928SPeter Avalos error("%s host key for %.200s was revoked and you have " 959856ea928SPeter Avalos "requested strict checking.", type, host); 960856ea928SPeter Avalos goto fail; 961856ea928SPeter Avalos } 962856ea928SPeter Avalos goto continue_unsafe; 963856ea928SPeter Avalos 96418de8d7fSPeter Avalos case HOST_CHANGED: 965856ea928SPeter Avalos if (want_cert) { 966856ea928SPeter Avalos /* 967856ea928SPeter Avalos * This is only a debug() since it is valid to have 968856ea928SPeter Avalos * CAs with wildcard DNS matches that don't match 969856ea928SPeter Avalos * all hosts that one might visit. 970856ea928SPeter Avalos */ 971856ea928SPeter Avalos debug("Host certificate authority does not " 9729f304aafSPeter Avalos "match %s in %s:%lu", CA_MARKER, 9739f304aafSPeter Avalos host_found->file, host_found->line); 974856ea928SPeter Avalos goto fail; 975856ea928SPeter Avalos } 97618de8d7fSPeter Avalos if (readonly == ROQUIET) 97718de8d7fSPeter Avalos goto fail; 97818de8d7fSPeter Avalos if (options.check_host_ip && host_ip_differ) { 97918de8d7fSPeter Avalos char *key_msg; 98018de8d7fSPeter Avalos if (ip_status == HOST_NEW) 98118de8d7fSPeter Avalos key_msg = "is unknown"; 98218de8d7fSPeter Avalos else if (ip_status == HOST_OK) 98318de8d7fSPeter Avalos key_msg = "is unchanged"; 98418de8d7fSPeter Avalos else 98518de8d7fSPeter Avalos key_msg = "has a different value"; 98618de8d7fSPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 98718de8d7fSPeter Avalos error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 98818de8d7fSPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 98918de8d7fSPeter Avalos error("The %s host key for %s has changed,", type, host); 99018de8d7fSPeter Avalos error("and the key for the corresponding IP address %s", ip); 99118de8d7fSPeter Avalos error("%s. This could either mean that", key_msg); 99218de8d7fSPeter Avalos error("DNS SPOOFING is happening or the IP address for the host"); 99318de8d7fSPeter Avalos error("and its host key have changed at the same time."); 99418de8d7fSPeter Avalos if (ip_status != HOST_NEW) 9959f304aafSPeter Avalos error("Offending key for IP in %s:%lu", 9969f304aafSPeter Avalos ip_found->file, ip_found->line); 99718de8d7fSPeter Avalos } 99818de8d7fSPeter Avalos /* The host key has changed. */ 99918de8d7fSPeter Avalos warn_changed_key(host_key); 100018de8d7fSPeter Avalos error("Add correct host key in %.100s to get rid of this message.", 10011c188a7fSPeter Avalos user_hostfiles[0]); 1002ce74bacaSMatthew Dillon error("Offending %s key in %s:%lu", 1003ce74bacaSMatthew Dillon sshkey_type(host_found->key), 10049f304aafSPeter Avalos host_found->file, host_found->line); 100518de8d7fSPeter Avalos 100618de8d7fSPeter Avalos /* 100718de8d7fSPeter Avalos * If strict host key checking is in use, the user will have 100818de8d7fSPeter Avalos * to edit the key manually and we can only abort. 100918de8d7fSPeter Avalos */ 1010ce74bacaSMatthew Dillon if (options.strict_host_key_checking != 1011ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_OFF) { 101218de8d7fSPeter Avalos error("%s host key for %.200s has changed and you have " 101318de8d7fSPeter Avalos "requested strict checking.", type, host); 101418de8d7fSPeter Avalos goto fail; 101518de8d7fSPeter Avalos } 101618de8d7fSPeter Avalos 1017856ea928SPeter Avalos continue_unsafe: 101818de8d7fSPeter Avalos /* 101918de8d7fSPeter Avalos * If strict host key checking has not been requested, allow 102018de8d7fSPeter Avalos * the connection but without MITM-able authentication or 102118de8d7fSPeter Avalos * forwarding. 102218de8d7fSPeter Avalos */ 102318de8d7fSPeter Avalos if (options.password_authentication) { 102418de8d7fSPeter Avalos error("Password authentication is disabled to avoid " 102518de8d7fSPeter Avalos "man-in-the-middle attacks."); 102618de8d7fSPeter Avalos options.password_authentication = 0; 102718de8d7fSPeter Avalos cancelled_forwarding = 1; 102818de8d7fSPeter Avalos } 102918de8d7fSPeter Avalos if (options.kbd_interactive_authentication) { 103018de8d7fSPeter Avalos error("Keyboard-interactive authentication is disabled" 103118de8d7fSPeter Avalos " to avoid man-in-the-middle attacks."); 103218de8d7fSPeter Avalos options.kbd_interactive_authentication = 0; 103318de8d7fSPeter Avalos options.challenge_response_authentication = 0; 103418de8d7fSPeter Avalos cancelled_forwarding = 1; 103518de8d7fSPeter Avalos } 103618de8d7fSPeter Avalos if (options.challenge_response_authentication) { 103718de8d7fSPeter Avalos error("Challenge/response authentication is disabled" 103818de8d7fSPeter Avalos " to avoid man-in-the-middle attacks."); 103918de8d7fSPeter Avalos options.challenge_response_authentication = 0; 104018de8d7fSPeter Avalos cancelled_forwarding = 1; 104118de8d7fSPeter Avalos } 104218de8d7fSPeter Avalos if (options.forward_agent) { 104318de8d7fSPeter Avalos error("Agent forwarding is disabled to avoid " 104418de8d7fSPeter Avalos "man-in-the-middle attacks."); 104518de8d7fSPeter Avalos options.forward_agent = 0; 104618de8d7fSPeter Avalos cancelled_forwarding = 1; 104718de8d7fSPeter Avalos } 104818de8d7fSPeter Avalos if (options.forward_x11) { 104918de8d7fSPeter Avalos error("X11 forwarding is disabled to avoid " 105018de8d7fSPeter Avalos "man-in-the-middle attacks."); 105118de8d7fSPeter Avalos options.forward_x11 = 0; 105218de8d7fSPeter Avalos cancelled_forwarding = 1; 105318de8d7fSPeter Avalos } 105418de8d7fSPeter Avalos if (options.num_local_forwards > 0 || 105518de8d7fSPeter Avalos options.num_remote_forwards > 0) { 105618de8d7fSPeter Avalos error("Port forwarding is disabled to avoid " 105718de8d7fSPeter Avalos "man-in-the-middle attacks."); 105818de8d7fSPeter Avalos options.num_local_forwards = 105918de8d7fSPeter Avalos options.num_remote_forwards = 0; 106018de8d7fSPeter Avalos cancelled_forwarding = 1; 106118de8d7fSPeter Avalos } 106218de8d7fSPeter Avalos if (options.tun_open != SSH_TUNMODE_NO) { 106318de8d7fSPeter Avalos error("Tunnel forwarding is disabled to avoid " 106418de8d7fSPeter Avalos "man-in-the-middle attacks."); 106518de8d7fSPeter Avalos options.tun_open = SSH_TUNMODE_NO; 106618de8d7fSPeter Avalos cancelled_forwarding = 1; 106718de8d7fSPeter Avalos } 106818de8d7fSPeter Avalos if (options.exit_on_forward_failure && cancelled_forwarding) 106918de8d7fSPeter Avalos fatal("Error: forwarding disabled due to host key " 107018de8d7fSPeter Avalos "check failure"); 107118de8d7fSPeter Avalos 107218de8d7fSPeter Avalos /* 107318de8d7fSPeter Avalos * XXX Should permit the user to change to use the new id. 107418de8d7fSPeter Avalos * This could be done by converting the host key to an 107518de8d7fSPeter Avalos * identifying sentence, tell that the host identifies itself 1076856ea928SPeter Avalos * by that sentence, and ask the user if he/she wishes to 107718de8d7fSPeter Avalos * accept the authentication. 107818de8d7fSPeter Avalos */ 107918de8d7fSPeter Avalos break; 108018de8d7fSPeter Avalos case HOST_FOUND: 108118de8d7fSPeter Avalos fatal("internal error"); 108218de8d7fSPeter Avalos break; 108318de8d7fSPeter Avalos } 108418de8d7fSPeter Avalos 108518de8d7fSPeter Avalos if (options.check_host_ip && host_status != HOST_CHANGED && 108618de8d7fSPeter Avalos ip_status == HOST_CHANGED) { 108718de8d7fSPeter Avalos snprintf(msg, sizeof(msg), 108818de8d7fSPeter Avalos "Warning: the %s host key for '%.200s' " 108918de8d7fSPeter Avalos "differs from the key for the IP address '%.128s'" 10909f304aafSPeter Avalos "\nOffending key for IP in %s:%lu", 10919f304aafSPeter Avalos type, host, ip, ip_found->file, ip_found->line); 109218de8d7fSPeter Avalos if (host_status == HOST_OK) { 109318de8d7fSPeter Avalos len = strlen(msg); 109418de8d7fSPeter Avalos snprintf(msg + len, sizeof(msg) - len, 10959f304aafSPeter Avalos "\nMatching host key in %s:%lu", 10969f304aafSPeter Avalos host_found->file, host_found->line); 109718de8d7fSPeter Avalos } 1098ce74bacaSMatthew Dillon if (options.strict_host_key_checking == 1099ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_ASK) { 110018de8d7fSPeter Avalos strlcat(msg, "\nAre you sure you want " 110118de8d7fSPeter Avalos "to continue connecting (yes/no)? ", sizeof(msg)); 1102664f4763Szrj if (!confirm(msg, NULL)) 110318de8d7fSPeter Avalos goto fail; 1104ce74bacaSMatthew Dillon } else if (options.strict_host_key_checking != 1105ce74bacaSMatthew Dillon SSH_STRICT_HOSTKEY_OFF) { 1106ce74bacaSMatthew Dillon logit("%s", msg); 1107ce74bacaSMatthew Dillon error("Exiting, you have requested strict checking."); 1108ce74bacaSMatthew Dillon goto fail; 110918de8d7fSPeter Avalos } else { 111018de8d7fSPeter Avalos logit("%s", msg); 111118de8d7fSPeter Avalos } 111218de8d7fSPeter Avalos } 111318de8d7fSPeter Avalos 1114e9778795SPeter Avalos if (!hostkey_trusted && options.update_hostkeys) { 1115e9778795SPeter Avalos debug("%s: hostkey not known or explicitly trusted: " 1116e9778795SPeter Avalos "disabling UpdateHostkeys", __func__); 1117e9778795SPeter Avalos options.update_hostkeys = 0; 1118e9778795SPeter Avalos } 1119e9778795SPeter Avalos 112036e94dc5SPeter Avalos free(ip); 112136e94dc5SPeter Avalos free(host); 11229f304aafSPeter Avalos if (host_hostkeys != NULL) 11239f304aafSPeter Avalos free_hostkeys(host_hostkeys); 11249f304aafSPeter Avalos if (ip_hostkeys != NULL) 11259f304aafSPeter Avalos free_hostkeys(ip_hostkeys); 112618de8d7fSPeter Avalos return 0; 112718de8d7fSPeter Avalos 112818de8d7fSPeter Avalos fail: 1129856ea928SPeter Avalos if (want_cert && host_status != HOST_REVOKED) { 1130856ea928SPeter Avalos /* 1131856ea928SPeter Avalos * No matching certificate. Downgrade cert to raw key and 1132856ea928SPeter Avalos * search normally. 1133856ea928SPeter Avalos */ 1134856ea928SPeter Avalos debug("No matching CA found. Retry with plain key"); 1135ce74bacaSMatthew Dillon if ((r = sshkey_from_private(host_key, &raw_key)) != 0) 1136ce74bacaSMatthew Dillon fatal("%s: sshkey_from_private: %s", 1137ce74bacaSMatthew Dillon __func__, ssh_err(r)); 1138ce74bacaSMatthew Dillon if ((r = sshkey_drop_cert(raw_key)) != 0) 1139ce74bacaSMatthew Dillon fatal("Couldn't drop certificate: %s", ssh_err(r)); 1140856ea928SPeter Avalos host_key = raw_key; 1141856ea928SPeter Avalos goto retry; 1142856ea928SPeter Avalos } 1143ce74bacaSMatthew Dillon sshkey_free(raw_key); 114436e94dc5SPeter Avalos free(ip); 114536e94dc5SPeter Avalos free(host); 11469f304aafSPeter Avalos if (host_hostkeys != NULL) 11479f304aafSPeter Avalos free_hostkeys(host_hostkeys); 11489f304aafSPeter Avalos if (ip_hostkeys != NULL) 11499f304aafSPeter Avalos free_hostkeys(ip_hostkeys); 115018de8d7fSPeter Avalos return -1; 115118de8d7fSPeter Avalos } 115218de8d7fSPeter Avalos 115318de8d7fSPeter Avalos /* returns 0 if key verifies or -1 if key does NOT verify */ 115418de8d7fSPeter Avalos int 1155ce74bacaSMatthew Dillon verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key) 115618de8d7fSPeter Avalos { 1157e9778795SPeter Avalos u_int i; 115836e94dc5SPeter Avalos int r = -1, flags = 0; 1159e9778795SPeter Avalos char valid[64], *fp = NULL, *cafp = NULL; 1160e9778795SPeter Avalos struct sshkey *plain = NULL; 11619f304aafSPeter Avalos 1162e9778795SPeter Avalos if ((fp = sshkey_fingerprint(host_key, 1163e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1164e9778795SPeter Avalos error("%s: fingerprint host key: %s", __func__, ssh_err(r)); 1165e9778795SPeter Avalos r = -1; 1166e9778795SPeter Avalos goto out; 1167e9778795SPeter Avalos } 116818de8d7fSPeter Avalos 1169e9778795SPeter Avalos if (sshkey_is_cert(host_key)) { 1170e9778795SPeter Avalos if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, 1171e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1172e9778795SPeter Avalos error("%s: fingerprint CA key: %s", 1173e9778795SPeter Avalos __func__, ssh_err(r)); 1174e9778795SPeter Avalos r = -1; 1175e9778795SPeter Avalos goto out; 1176e9778795SPeter Avalos } 1177e9778795SPeter Avalos sshkey_format_cert_validity(host_key->cert, 1178e9778795SPeter Avalos valid, sizeof(valid)); 1179e9778795SPeter Avalos debug("Server host certificate: %s %s, serial %llu " 1180e9778795SPeter Avalos "ID \"%s\" CA %s %s valid %s", 1181e9778795SPeter Avalos sshkey_ssh_name(host_key), fp, 1182e9778795SPeter Avalos (unsigned long long)host_key->cert->serial, 1183e9778795SPeter Avalos host_key->cert->key_id, 1184e9778795SPeter Avalos sshkey_ssh_name(host_key->cert->signature_key), cafp, 1185e9778795SPeter Avalos valid); 1186e9778795SPeter Avalos for (i = 0; i < host_key->cert->nprincipals; i++) { 1187e9778795SPeter Avalos debug2("Server host certificate hostname: %s", 1188e9778795SPeter Avalos host_key->cert->principals[i]); 1189e9778795SPeter Avalos } 1190e9778795SPeter Avalos } else { 1191ce74bacaSMatthew Dillon debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp); 1192e9778795SPeter Avalos } 1193e9778795SPeter Avalos 1194e9778795SPeter Avalos if (sshkey_equal(previous_host_key, host_key)) { 1195e9778795SPeter Avalos debug2("%s: server host key %s %s matches cached key", 1196e9778795SPeter Avalos __func__, sshkey_type(host_key), fp); 1197e9778795SPeter Avalos r = 0; 1198e9778795SPeter Avalos goto out; 1199e9778795SPeter Avalos } 1200e9778795SPeter Avalos 1201e9778795SPeter Avalos /* Check in RevokedHostKeys file if specified */ 1202e9778795SPeter Avalos if (options.revoked_host_keys != NULL) { 1203e9778795SPeter Avalos r = sshkey_check_revoked(host_key, options.revoked_host_keys); 1204e9778795SPeter Avalos switch (r) { 1205e9778795SPeter Avalos case 0: 1206e9778795SPeter Avalos break; /* not revoked */ 1207e9778795SPeter Avalos case SSH_ERR_KEY_REVOKED: 1208e9778795SPeter Avalos error("Host key %s %s revoked by file %s", 1209e9778795SPeter Avalos sshkey_type(host_key), fp, 1210e9778795SPeter Avalos options.revoked_host_keys); 1211e9778795SPeter Avalos r = -1; 1212e9778795SPeter Avalos goto out; 1213e9778795SPeter Avalos default: 1214e9778795SPeter Avalos error("Error checking host key %s %s in " 1215e9778795SPeter Avalos "revoked keys file %s: %s", sshkey_type(host_key), 1216e9778795SPeter Avalos fp, options.revoked_host_keys, ssh_err(r)); 1217e9778795SPeter Avalos r = -1; 1218e9778795SPeter Avalos goto out; 1219e9778795SPeter Avalos } 122036e94dc5SPeter Avalos } 122136e94dc5SPeter Avalos 122236e94dc5SPeter Avalos if (options.verify_host_key_dns) { 122336e94dc5SPeter Avalos /* 122436e94dc5SPeter Avalos * XXX certs are not yet supported for DNS, so downgrade 122536e94dc5SPeter Avalos * them and try the plain key. 122636e94dc5SPeter Avalos */ 1227e9778795SPeter Avalos if ((r = sshkey_from_private(host_key, &plain)) != 0) 1228e9778795SPeter Avalos goto out; 1229e9778795SPeter Avalos if (sshkey_is_cert(plain)) 1230e9778795SPeter Avalos sshkey_drop_cert(plain); 123136e94dc5SPeter Avalos if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) { 123218de8d7fSPeter Avalos if (flags & DNS_VERIFY_FOUND) { 123318de8d7fSPeter Avalos if (options.verify_host_key_dns == 1 && 123418de8d7fSPeter Avalos flags & DNS_VERIFY_MATCH && 123536e94dc5SPeter Avalos flags & DNS_VERIFY_SECURE) { 123636e94dc5SPeter Avalos r = 0; 1237e9778795SPeter Avalos goto out; 123836e94dc5SPeter Avalos } 123918de8d7fSPeter Avalos if (flags & DNS_VERIFY_MATCH) { 124018de8d7fSPeter Avalos matching_host_key_dns = 1; 124118de8d7fSPeter Avalos } else { 124236e94dc5SPeter Avalos warn_changed_key(plain); 124336e94dc5SPeter Avalos error("Update the SSHFP RR in DNS " 124436e94dc5SPeter Avalos "with the new host key to get rid " 124536e94dc5SPeter Avalos "of this message."); 124618de8d7fSPeter Avalos } 124718de8d7fSPeter Avalos } 124818de8d7fSPeter Avalos } 124936e94dc5SPeter Avalos } 125036e94dc5SPeter Avalos r = check_host_key(host, hostaddr, options.port, host_key, RDRW, 12511c188a7fSPeter Avalos options.user_hostfiles, options.num_user_hostfiles, 12521c188a7fSPeter Avalos options.system_hostfiles, options.num_system_hostfiles); 125336e94dc5SPeter Avalos 1254e9778795SPeter Avalos out: 1255e9778795SPeter Avalos sshkey_free(plain); 1256e9778795SPeter Avalos free(fp); 1257e9778795SPeter Avalos free(cafp); 125836e94dc5SPeter Avalos if (r == 0 && host_key != NULL) { 1259ce74bacaSMatthew Dillon sshkey_free(previous_host_key); 1260ce74bacaSMatthew Dillon r = sshkey_from_private(host_key, &previous_host_key); 126136e94dc5SPeter Avalos } 126236e94dc5SPeter Avalos 126336e94dc5SPeter Avalos return r; 126418de8d7fSPeter Avalos } 126518de8d7fSPeter Avalos 126618de8d7fSPeter Avalos /* 126718de8d7fSPeter Avalos * Starts a dialog with the server, and authenticates the current user on the 126818de8d7fSPeter Avalos * server. This does not need any extra privileges. The basic connection 126918de8d7fSPeter Avalos * to the server must already have been established before this is called. 127018de8d7fSPeter Avalos * If login fails, this function prints an error and never returns. 127118de8d7fSPeter Avalos * This function does not require super-user privileges. 127218de8d7fSPeter Avalos */ 127318de8d7fSPeter Avalos void 1274664f4763Szrj ssh_login(struct ssh *ssh, Sensitive *sensitive, const char *orighost, 12759f304aafSPeter Avalos struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) 127618de8d7fSPeter Avalos { 127736e94dc5SPeter Avalos char *host; 127818de8d7fSPeter Avalos char *server_user, *local_user; 1279*0cbfa66cSDaniel Fojt int r; 128018de8d7fSPeter Avalos 128118de8d7fSPeter Avalos local_user = xstrdup(pw->pw_name); 128218de8d7fSPeter Avalos server_user = options.user ? options.user : local_user; 128318de8d7fSPeter Avalos 128418de8d7fSPeter Avalos /* Convert the user-supplied hostname into all lowercase. */ 128518de8d7fSPeter Avalos host = xstrdup(orighost); 128636e94dc5SPeter Avalos lowercase(host); 128718de8d7fSPeter Avalos 128818de8d7fSPeter Avalos /* Exchange protocol version identification strings with the server. */ 1289*0cbfa66cSDaniel Fojt if ((r = kex_exchange_identification(ssh, timeout_ms, NULL)) != 0) 1290*0cbfa66cSDaniel Fojt sshpkt_fatal(ssh, r, "banner exchange"); 129118de8d7fSPeter Avalos 129218de8d7fSPeter Avalos /* Put the connection into non-blocking mode. */ 1293664f4763Szrj ssh_packet_set_nonblocking(ssh); 129418de8d7fSPeter Avalos 129518de8d7fSPeter Avalos /* key exchange */ 129618de8d7fSPeter Avalos /* authenticate user */ 1297e9778795SPeter Avalos debug("Authenticating to %s:%d as '%s'", host, port, server_user); 1298664f4763Szrj ssh_kex2(ssh, host, hostaddr, port); 1299664f4763Szrj ssh_userauth2(ssh, local_user, server_user, host, sensitive); 130036e94dc5SPeter Avalos free(local_user); 1301*0cbfa66cSDaniel Fojt free(host); 130218de8d7fSPeter Avalos } 130318de8d7fSPeter Avalos 130418de8d7fSPeter Avalos /* print all known host keys for a given host, but skip keys of given type */ 130518de8d7fSPeter Avalos static int 1306ce74bacaSMatthew Dillon show_other_keys(struct hostkeys *hostkeys, struct sshkey *key) 130718de8d7fSPeter Avalos { 130836e94dc5SPeter Avalos int type[] = { 130936e94dc5SPeter Avalos KEY_RSA, 131036e94dc5SPeter Avalos KEY_DSA, 131136e94dc5SPeter Avalos KEY_ECDSA, 131236e94dc5SPeter Avalos KEY_ED25519, 1313664f4763Szrj KEY_XMSS, 131436e94dc5SPeter Avalos -1 131536e94dc5SPeter Avalos }; 13169f304aafSPeter Avalos int i, ret = 0; 13179f304aafSPeter Avalos char *fp, *ra; 13189f304aafSPeter Avalos const struct hostkey_entry *found; 131918de8d7fSPeter Avalos 132018de8d7fSPeter Avalos for (i = 0; type[i] != -1; i++) { 132118de8d7fSPeter Avalos if (type[i] == key->type) 132218de8d7fSPeter Avalos continue; 13239f304aafSPeter Avalos if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) 132418de8d7fSPeter Avalos continue; 1325e9778795SPeter Avalos fp = sshkey_fingerprint(found->key, 1326e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_DEFAULT); 1327e9778795SPeter Avalos ra = sshkey_fingerprint(found->key, 1328e9778795SPeter Avalos options.fingerprint_hash, SSH_FP_RANDOMART); 1329e9778795SPeter Avalos if (fp == NULL || ra == NULL) 1330e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 13319f304aafSPeter Avalos logit("WARNING: %s key found for host %s\n" 13329f304aafSPeter Avalos "in %s:%lu\n" 13339f304aafSPeter Avalos "%s key fingerprint %s.", 1334664f4763Szrj sshkey_type(found->key), 13359f304aafSPeter Avalos found->host, found->file, found->line, 1336664f4763Szrj sshkey_type(found->key), fp); 13379f304aafSPeter Avalos if (options.visual_host_key) 13389f304aafSPeter Avalos logit("%s", ra); 133936e94dc5SPeter Avalos free(ra); 134036e94dc5SPeter Avalos free(fp); 13419f304aafSPeter Avalos ret = 1; 134218de8d7fSPeter Avalos } 13439f304aafSPeter Avalos return ret; 134418de8d7fSPeter Avalos } 134518de8d7fSPeter Avalos 134618de8d7fSPeter Avalos static void 1347ce74bacaSMatthew Dillon warn_changed_key(struct sshkey *host_key) 134818de8d7fSPeter Avalos { 134918de8d7fSPeter Avalos char *fp; 135018de8d7fSPeter Avalos 1351e9778795SPeter Avalos fp = sshkey_fingerprint(host_key, options.fingerprint_hash, 1352e9778795SPeter Avalos SSH_FP_DEFAULT); 1353e9778795SPeter Avalos if (fp == NULL) 1354e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 135518de8d7fSPeter Avalos 135618de8d7fSPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 135718de8d7fSPeter Avalos error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 135818de8d7fSPeter Avalos error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 135918de8d7fSPeter Avalos error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 136018de8d7fSPeter Avalos error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 13619f304aafSPeter Avalos error("It is also possible that a host key has just been changed."); 136218de8d7fSPeter Avalos error("The fingerprint for the %s key sent by the remote host is\n%s.", 1363664f4763Szrj sshkey_type(host_key), fp); 136418de8d7fSPeter Avalos error("Please contact your system administrator."); 136518de8d7fSPeter Avalos 136636e94dc5SPeter Avalos free(fp); 136718de8d7fSPeter Avalos } 136818de8d7fSPeter Avalos 136918de8d7fSPeter Avalos /* 137018de8d7fSPeter Avalos * Execute a local command 137118de8d7fSPeter Avalos */ 137218de8d7fSPeter Avalos int 137318de8d7fSPeter Avalos ssh_local_cmd(const char *args) 137418de8d7fSPeter Avalos { 137518de8d7fSPeter Avalos char *shell; 137618de8d7fSPeter Avalos pid_t pid; 137718de8d7fSPeter Avalos int status; 13789f304aafSPeter Avalos void (*osighand)(int); 137918de8d7fSPeter Avalos 138018de8d7fSPeter Avalos if (!options.permit_local_command || 138118de8d7fSPeter Avalos args == NULL || !*args) 138218de8d7fSPeter Avalos return (1); 138318de8d7fSPeter Avalos 13849f304aafSPeter Avalos if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 138518de8d7fSPeter Avalos shell = _PATH_BSHELL; 138618de8d7fSPeter Avalos 1387*0cbfa66cSDaniel Fojt osighand = ssh_signal(SIGCHLD, SIG_DFL); 138818de8d7fSPeter Avalos pid = fork(); 138918de8d7fSPeter Avalos if (pid == 0) { 1390*0cbfa66cSDaniel Fojt ssh_signal(SIGPIPE, SIG_DFL); 139118de8d7fSPeter Avalos debug3("Executing %s -c \"%s\"", shell, args); 139218de8d7fSPeter Avalos execl(shell, shell, "-c", args, (char *)NULL); 139318de8d7fSPeter Avalos error("Couldn't execute %s -c \"%s\": %s", 139418de8d7fSPeter Avalos shell, args, strerror(errno)); 139518de8d7fSPeter Avalos _exit(1); 139618de8d7fSPeter Avalos } else if (pid == -1) 139718de8d7fSPeter Avalos fatal("fork failed: %.100s", strerror(errno)); 139818de8d7fSPeter Avalos while (waitpid(pid, &status, 0) == -1) 139918de8d7fSPeter Avalos if (errno != EINTR) 140018de8d7fSPeter Avalos fatal("Couldn't wait for child: %s", strerror(errno)); 1401*0cbfa66cSDaniel Fojt ssh_signal(SIGCHLD, osighand); 140218de8d7fSPeter Avalos 140318de8d7fSPeter Avalos if (!WIFEXITED(status)) 140418de8d7fSPeter Avalos return (1); 140518de8d7fSPeter Avalos 140618de8d7fSPeter Avalos return (WEXITSTATUS(status)); 140718de8d7fSPeter Avalos } 1408e9778795SPeter Avalos 1409e9778795SPeter Avalos void 1410*0cbfa66cSDaniel Fojt maybe_add_key_to_agent(const char *authfile, struct sshkey *private, 1411*0cbfa66cSDaniel Fojt const char *comment, const char *passphrase) 1412e9778795SPeter Avalos { 1413e9778795SPeter Avalos int auth_sock = -1, r; 1414*0cbfa66cSDaniel Fojt const char *skprovider = NULL; 1415e9778795SPeter Avalos 1416e9778795SPeter Avalos if (options.add_keys_to_agent == 0) 1417e9778795SPeter Avalos return; 1418e9778795SPeter Avalos 1419e9778795SPeter Avalos if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { 1420e9778795SPeter Avalos debug3("no authentication agent, not adding key"); 1421e9778795SPeter Avalos return; 1422e9778795SPeter Avalos } 1423e9778795SPeter Avalos 1424e9778795SPeter Avalos if (options.add_keys_to_agent == 2 && 1425e9778795SPeter Avalos !ask_permission("Add key %s (%s) to agent?", authfile, comment)) { 1426e9778795SPeter Avalos debug3("user denied adding this key"); 1427ce74bacaSMatthew Dillon close(auth_sock); 1428e9778795SPeter Avalos return; 1429e9778795SPeter Avalos } 1430*0cbfa66cSDaniel Fojt if (sshkey_is_sk(private)) 1431*0cbfa66cSDaniel Fojt skprovider = options.sk_provider; 1432*0cbfa66cSDaniel Fojt if ((r = ssh_add_identity_constrained(auth_sock, private, 1433*0cbfa66cSDaniel Fojt comment == NULL ? authfile : comment, 0, 1434*0cbfa66cSDaniel Fojt (options.add_keys_to_agent == 3), 0, skprovider)) == 0) 1435e9778795SPeter Avalos debug("identity added to agent: %s", authfile); 1436e9778795SPeter Avalos else 1437e9778795SPeter Avalos debug("could not add identity to agent: %s (%d)", authfile, r); 1438ce74bacaSMatthew Dillon close(auth_sock); 1439e9778795SPeter Avalos } 1440