1*190cef3dSDag-Erling Smørgrav /* $OpenBSD: sshconnect.c,v 1.304 2018/07/27 05:34:42 dtucker Exp $ */ 2511b41d2SMark Murray /* 3511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 4511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5511b41d2SMark Murray * All rights reserved 6511b41d2SMark Murray * Code to connect to a remote host, and to perform the client side of the 7511b41d2SMark Murray * login (authentication) dialog. 842f71286SMark Murray * 9c2d3a559SKris Kennaway * As far as I am concerned, the code I have written for this software 10c2d3a559SKris Kennaway * can be used freely for any purpose. Any derived versions of this 11c2d3a559SKris Kennaway * software must be clearly marked as such, and if the derived work is 12c2d3a559SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 13c2d3a559SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 14511b41d2SMark Murray */ 15511b41d2SMark Murray 16511b41d2SMark Murray #include "includes.h" 17cf783db1SDag-Erling Smørgrav __RCSID("$FreeBSD$"); 18511b41d2SMark Murray 19333ee039SDag-Erling Smørgrav #include <sys/types.h> 20333ee039SDag-Erling Smørgrav #include <sys/wait.h> 21333ee039SDag-Erling Smørgrav #include <sys/stat.h> 22333ee039SDag-Erling Smørgrav #include <sys/socket.h> 23333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 24333ee039SDag-Erling Smørgrav # include <sys/time.h> 25333ee039SDag-Erling Smørgrav #endif 26e8aafc91SKris Kennaway 2747dd1d1bSDag-Erling Smørgrav #include <net/if.h> 28333ee039SDag-Erling Smørgrav #include <netinet/in.h> 29333ee039SDag-Erling Smørgrav #include <arpa/inet.h> 30f7167e0eSDag-Erling Smørgrav #include <rpc/rpc.h> 31333ee039SDag-Erling Smørgrav 32333ee039SDag-Erling Smørgrav #include <ctype.h> 33333ee039SDag-Erling Smørgrav #include <errno.h> 34b15c8340SDag-Erling Smørgrav #include <fcntl.h> 35333ee039SDag-Erling Smørgrav #include <netdb.h> 36333ee039SDag-Erling Smørgrav #ifdef HAVE_PATHS_H 37333ee039SDag-Erling Smørgrav #include <paths.h> 38333ee039SDag-Erling Smørgrav #endif 39333ee039SDag-Erling Smørgrav #include <pwd.h> 404f52dfbbSDag-Erling Smørgrav #ifdef HAVE_POLL_H 414f52dfbbSDag-Erling Smørgrav #include <poll.h> 424f52dfbbSDag-Erling Smørgrav #endif 434a421b63SDag-Erling Smørgrav #include <signal.h> 44333ee039SDag-Erling Smørgrav #include <stdarg.h> 45333ee039SDag-Erling Smørgrav #include <stdio.h> 46333ee039SDag-Erling Smørgrav #include <stdlib.h> 47333ee039SDag-Erling Smørgrav #include <string.h> 48333ee039SDag-Erling Smørgrav #include <unistd.h> 4947dd1d1bSDag-Erling Smørgrav #ifdef HAVE_IFADDRS_H 5047dd1d1bSDag-Erling Smørgrav # include <ifaddrs.h> 5147dd1d1bSDag-Erling Smørgrav #endif 52333ee039SDag-Erling Smørgrav 53511b41d2SMark Murray #include "xmalloc.h" 54333ee039SDag-Erling Smørgrav #include "hostfile.h" 55333ee039SDag-Erling Smørgrav #include "ssh.h" 56*190cef3dSDag-Erling Smørgrav #include "sshbuf.h" 57511b41d2SMark Murray #include "packet.h" 58511b41d2SMark Murray #include "compat.h" 59*190cef3dSDag-Erling Smørgrav #include "sshkey.h" 60e8aafc91SKris Kennaway #include "sshconnect.h" 613c6ae118SKris Kennaway #include "hostfile.h" 62ca3176e7SBrian Feldman #include "log.h" 63a0ee8cc6SDag-Erling Smørgrav #include "misc.h" 64ca3176e7SBrian Feldman #include "readconf.h" 65ca3176e7SBrian Feldman #include "atomicio.h" 66cf2b5f3bSDag-Erling Smørgrav #include "dns.h" 67f7167e0eSDag-Erling Smørgrav #include "monitor_fdpass.h" 68b15c8340SDag-Erling Smørgrav #include "ssh2.h" 69333ee039SDag-Erling Smørgrav #include "version.h" 70bc5531deSDag-Erling Smørgrav #include "authfile.h" 71bc5531deSDag-Erling Smørgrav #include "ssherr.h" 72acc1a9efSDag-Erling Smørgrav #include "authfd.h" 73cf2b5f3bSDag-Erling Smørgrav 74e8aafc91SKris Kennaway char *client_version_string = NULL; 75e8aafc91SKris Kennaway char *server_version_string = NULL; 764f52dfbbSDag-Erling Smørgrav struct sshkey *previous_host_key = NULL; 77511b41d2SMark Murray 78b74df5b2SDag-Erling Smørgrav static int matching_host_key_dns = 0; 79cf2b5f3bSDag-Erling Smørgrav 804a421b63SDag-Erling Smørgrav static pid_t proxy_command_pid = 0; 814a421b63SDag-Erling Smørgrav 8280628bacSDag-Erling Smørgrav /* import */ 83511b41d2SMark Murray extern Options options; 84511b41d2SMark Murray extern char *__progname; 85511b41d2SMark Murray 864f52dfbbSDag-Erling Smørgrav static int show_other_keys(struct hostkeys *, struct sshkey *); 874f52dfbbSDag-Erling Smørgrav static void warn_changed_key(struct sshkey *); 88ca3176e7SBrian Feldman 89f7167e0eSDag-Erling Smørgrav /* Expand a proxy command */ 90f7167e0eSDag-Erling Smørgrav static char * 91f7167e0eSDag-Erling Smørgrav expand_proxy_command(const char *proxy_command, const char *user, 92f7167e0eSDag-Erling Smørgrav const char *host, int port) 93f7167e0eSDag-Erling Smørgrav { 94f7167e0eSDag-Erling Smørgrav char *tmp, *ret, strport[NI_MAXSERV]; 95f7167e0eSDag-Erling Smørgrav 96f7167e0eSDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", port); 97f7167e0eSDag-Erling Smørgrav xasprintf(&tmp, "exec %s", proxy_command); 98f7167e0eSDag-Erling Smørgrav ret = percent_expand(tmp, "h", host, "p", strport, 99f7167e0eSDag-Erling Smørgrav "r", options.user, (char *)NULL); 100f7167e0eSDag-Erling Smørgrav free(tmp); 101f7167e0eSDag-Erling Smørgrav return ret; 102f7167e0eSDag-Erling Smørgrav } 103f7167e0eSDag-Erling Smørgrav 104f7167e0eSDag-Erling Smørgrav /* 105f7167e0eSDag-Erling Smørgrav * Connect to the given ssh server using a proxy command that passes a 106f7167e0eSDag-Erling Smørgrav * a connected fd back to us. 107f7167e0eSDag-Erling Smørgrav */ 108f7167e0eSDag-Erling Smørgrav static int 1094f52dfbbSDag-Erling Smørgrav ssh_proxy_fdpass_connect(struct ssh *ssh, const char *host, u_short port, 110f7167e0eSDag-Erling Smørgrav const char *proxy_command) 111f7167e0eSDag-Erling Smørgrav { 112f7167e0eSDag-Erling Smørgrav char *command_string; 113f7167e0eSDag-Erling Smørgrav int sp[2], sock; 114f7167e0eSDag-Erling Smørgrav pid_t pid; 115f7167e0eSDag-Erling Smørgrav char *shell; 116f7167e0eSDag-Erling Smørgrav 117f7167e0eSDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL) 118f7167e0eSDag-Erling Smørgrav shell = _PATH_BSHELL; 119f7167e0eSDag-Erling Smørgrav 120f7167e0eSDag-Erling Smørgrav if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) 121f7167e0eSDag-Erling Smørgrav fatal("Could not create socketpair to communicate with " 122f7167e0eSDag-Erling Smørgrav "proxy dialer: %.100s", strerror(errno)); 123f7167e0eSDag-Erling Smørgrav 124f7167e0eSDag-Erling Smørgrav command_string = expand_proxy_command(proxy_command, options.user, 125f7167e0eSDag-Erling Smørgrav host, port); 126f7167e0eSDag-Erling Smørgrav debug("Executing proxy dialer command: %.500s", command_string); 127f7167e0eSDag-Erling Smørgrav 128f7167e0eSDag-Erling Smørgrav /* Fork and execute the proxy command. */ 129f7167e0eSDag-Erling Smørgrav if ((pid = fork()) == 0) { 130f7167e0eSDag-Erling Smørgrav char *argv[10]; 131f7167e0eSDag-Erling Smørgrav 132f7167e0eSDag-Erling Smørgrav close(sp[1]); 133f7167e0eSDag-Erling Smørgrav /* Redirect stdin and stdout. */ 134f7167e0eSDag-Erling Smørgrav if (sp[0] != 0) { 135f7167e0eSDag-Erling Smørgrav if (dup2(sp[0], 0) < 0) 136f7167e0eSDag-Erling Smørgrav perror("dup2 stdin"); 137f7167e0eSDag-Erling Smørgrav } 138f7167e0eSDag-Erling Smørgrav if (sp[0] != 1) { 139f7167e0eSDag-Erling Smørgrav if (dup2(sp[0], 1) < 0) 140f7167e0eSDag-Erling Smørgrav perror("dup2 stdout"); 141f7167e0eSDag-Erling Smørgrav } 142f7167e0eSDag-Erling Smørgrav if (sp[0] >= 2) 143f7167e0eSDag-Erling Smørgrav close(sp[0]); 144f7167e0eSDag-Erling Smørgrav 145f7167e0eSDag-Erling Smørgrav /* 146f7167e0eSDag-Erling Smørgrav * Stderr is left as it is so that error messages get 147f7167e0eSDag-Erling Smørgrav * printed on the user's terminal. 148f7167e0eSDag-Erling Smørgrav */ 149f7167e0eSDag-Erling Smørgrav argv[0] = shell; 150f7167e0eSDag-Erling Smørgrav argv[1] = "-c"; 151f7167e0eSDag-Erling Smørgrav argv[2] = command_string; 152f7167e0eSDag-Erling Smørgrav argv[3] = NULL; 153f7167e0eSDag-Erling Smørgrav 154f7167e0eSDag-Erling Smørgrav /* 155f7167e0eSDag-Erling Smørgrav * Execute the proxy command. 156f7167e0eSDag-Erling Smørgrav * Note that we gave up any extra privileges above. 157f7167e0eSDag-Erling Smørgrav */ 158f7167e0eSDag-Erling Smørgrav execv(argv[0], argv); 159f7167e0eSDag-Erling Smørgrav perror(argv[0]); 160f7167e0eSDag-Erling Smørgrav exit(1); 161f7167e0eSDag-Erling Smørgrav } 162f7167e0eSDag-Erling Smørgrav /* Parent. */ 163f7167e0eSDag-Erling Smørgrav if (pid < 0) 164f7167e0eSDag-Erling Smørgrav fatal("fork failed: %.100s", strerror(errno)); 165f7167e0eSDag-Erling Smørgrav close(sp[0]); 166f7167e0eSDag-Erling Smørgrav free(command_string); 167f7167e0eSDag-Erling Smørgrav 168f7167e0eSDag-Erling Smørgrav if ((sock = mm_receive_fd(sp[1])) == -1) 169f7167e0eSDag-Erling Smørgrav fatal("proxy dialer did not pass back a connection"); 170acc1a9efSDag-Erling Smørgrav close(sp[1]); 171f7167e0eSDag-Erling Smørgrav 172f7167e0eSDag-Erling Smørgrav while (waitpid(pid, NULL, 0) == -1) 173f7167e0eSDag-Erling Smørgrav if (errno != EINTR) 174f7167e0eSDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 175f7167e0eSDag-Erling Smørgrav 176f7167e0eSDag-Erling Smørgrav /* Set the connection file descriptors. */ 1774f52dfbbSDag-Erling Smørgrav if (ssh_packet_set_connection(ssh, sock, sock) == NULL) 1784f52dfbbSDag-Erling Smørgrav return -1; /* ssh_packet_set_connection logs error */ 179f7167e0eSDag-Erling Smørgrav 180f7167e0eSDag-Erling Smørgrav return 0; 181f7167e0eSDag-Erling Smørgrav } 182f7167e0eSDag-Erling Smørgrav 183511b41d2SMark Murray /* 184511b41d2SMark Murray * Connect to the given ssh server using a proxy command. 185511b41d2SMark Murray */ 186af12a3e7SDag-Erling Smørgrav static int 1874f52dfbbSDag-Erling Smørgrav ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port, 1884f52dfbbSDag-Erling Smørgrav const char *proxy_command) 189511b41d2SMark Murray { 190f7167e0eSDag-Erling Smørgrav char *command_string; 191511b41d2SMark Murray int pin[2], pout[2]; 192e8aafc91SKris Kennaway pid_t pid; 193f7167e0eSDag-Erling Smørgrav char *shell; 194420bce64SDag-Erling Smørgrav 1954a421b63SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 196d4af9e69SDag-Erling Smørgrav shell = _PATH_BSHELL; 197511b41d2SMark Murray 198511b41d2SMark Murray /* Create pipes for communicating with the proxy. */ 199511b41d2SMark Murray if (pipe(pin) < 0 || pipe(pout) < 0) 200511b41d2SMark Murray fatal("Could not create pipes to communicate with the proxy: %.100s", 201511b41d2SMark Murray strerror(errno)); 202511b41d2SMark Murray 203f7167e0eSDag-Erling Smørgrav command_string = expand_proxy_command(proxy_command, options.user, 204f7167e0eSDag-Erling Smørgrav host, port); 205511b41d2SMark Murray debug("Executing proxy command: %.500s", command_string); 206511b41d2SMark Murray 207511b41d2SMark Murray /* Fork and execute the proxy command. */ 208511b41d2SMark Murray if ((pid = fork()) == 0) { 209511b41d2SMark Murray char *argv[10]; 210511b41d2SMark Murray 211511b41d2SMark Murray /* Redirect stdin and stdout. */ 212511b41d2SMark Murray close(pin[1]); 213511b41d2SMark Murray if (pin[0] != 0) { 214511b41d2SMark Murray if (dup2(pin[0], 0) < 0) 215511b41d2SMark Murray perror("dup2 stdin"); 216511b41d2SMark Murray close(pin[0]); 217511b41d2SMark Murray } 218511b41d2SMark Murray close(pout[0]); 219511b41d2SMark Murray if (dup2(pout[1], 1) < 0) 220511b41d2SMark Murray perror("dup2 stdout"); 221511b41d2SMark Murray /* Cannot be 1 because pin allocated two descriptors. */ 222511b41d2SMark Murray close(pout[1]); 223511b41d2SMark Murray 224511b41d2SMark Murray /* Stderr is left as it is so that error messages get 225511b41d2SMark Murray printed on the user's terminal. */ 226d4af9e69SDag-Erling Smørgrav argv[0] = shell; 227511b41d2SMark Murray argv[1] = "-c"; 228511b41d2SMark Murray argv[2] = command_string; 229511b41d2SMark Murray argv[3] = NULL; 230511b41d2SMark Murray 231511b41d2SMark Murray /* Execute the proxy command. Note that we gave up any 232511b41d2SMark Murray extra privileges above. */ 2334a421b63SDag-Erling Smørgrav signal(SIGPIPE, SIG_DFL); 234ca3176e7SBrian Feldman execv(argv[0], argv); 235ca3176e7SBrian Feldman perror(argv[0]); 236511b41d2SMark Murray exit(1); 237511b41d2SMark Murray } 238511b41d2SMark Murray /* Parent. */ 239511b41d2SMark Murray if (pid < 0) 240511b41d2SMark Murray fatal("fork failed: %.100s", strerror(errno)); 241f388f5efSDag-Erling Smørgrav else 242f388f5efSDag-Erling Smørgrav proxy_command_pid = pid; /* save pid to clean up later */ 243511b41d2SMark Murray 244511b41d2SMark Murray /* Close child side of the descriptors. */ 245511b41d2SMark Murray close(pin[0]); 246511b41d2SMark Murray close(pout[1]); 247511b41d2SMark Murray 248511b41d2SMark Murray /* Free the command name. */ 249e4a9863fSDag-Erling Smørgrav free(command_string); 250511b41d2SMark Murray 251511b41d2SMark Murray /* Set the connection file descriptors. */ 2524f52dfbbSDag-Erling Smørgrav if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL) 2534f52dfbbSDag-Erling Smørgrav return -1; /* ssh_packet_set_connection logs error */ 254511b41d2SMark Murray 255af12a3e7SDag-Erling Smørgrav return 0; 256511b41d2SMark Murray } 257511b41d2SMark Murray 2584a421b63SDag-Erling Smørgrav void 2594a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(void) 2604a421b63SDag-Erling Smørgrav { 2614a421b63SDag-Erling Smørgrav /* 2624a421b63SDag-Erling Smørgrav * Send SIGHUP to proxy command if used. We don't wait() in 2634a421b63SDag-Erling Smørgrav * case it hangs and instead rely on init to reap the child 2644a421b63SDag-Erling Smørgrav */ 2654a421b63SDag-Erling Smørgrav if (proxy_command_pid > 1) 2664a421b63SDag-Erling Smørgrav kill(proxy_command_pid, SIGHUP); 2674a421b63SDag-Erling Smørgrav } 2684a421b63SDag-Erling Smørgrav 26947dd1d1bSDag-Erling Smørgrav #ifdef HAVE_IFADDRS_H 27047dd1d1bSDag-Erling Smørgrav /* 27147dd1d1bSDag-Erling Smørgrav * Search a interface address list (returned from getifaddrs(3)) for an 272*190cef3dSDag-Erling Smørgrav * address that matches the desired address family on the specified interface. 27347dd1d1bSDag-Erling Smørgrav * Returns 0 and fills in *resultp and *rlenp on success. Returns -1 on failure. 27447dd1d1bSDag-Erling Smørgrav */ 27547dd1d1bSDag-Erling Smørgrav static int 27647dd1d1bSDag-Erling Smørgrav check_ifaddrs(const char *ifname, int af, const struct ifaddrs *ifaddrs, 27747dd1d1bSDag-Erling Smørgrav struct sockaddr_storage *resultp, socklen_t *rlenp) 27847dd1d1bSDag-Erling Smørgrav { 27947dd1d1bSDag-Erling Smørgrav struct sockaddr_in6 *sa6; 28047dd1d1bSDag-Erling Smørgrav struct sockaddr_in *sa; 28147dd1d1bSDag-Erling Smørgrav struct in6_addr *v6addr; 28247dd1d1bSDag-Erling Smørgrav const struct ifaddrs *ifa; 28347dd1d1bSDag-Erling Smørgrav int allow_local; 28447dd1d1bSDag-Erling Smørgrav 28547dd1d1bSDag-Erling Smørgrav /* 28647dd1d1bSDag-Erling Smørgrav * Prefer addresses that are not loopback or linklocal, but use them 28747dd1d1bSDag-Erling Smørgrav * if nothing else matches. 28847dd1d1bSDag-Erling Smørgrav */ 28947dd1d1bSDag-Erling Smørgrav for (allow_local = 0; allow_local < 2; allow_local++) { 29047dd1d1bSDag-Erling Smørgrav for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 29147dd1d1bSDag-Erling Smørgrav if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL || 29247dd1d1bSDag-Erling Smørgrav (ifa->ifa_flags & IFF_UP) == 0 || 29347dd1d1bSDag-Erling Smørgrav ifa->ifa_addr->sa_family != af || 29447dd1d1bSDag-Erling Smørgrav strcmp(ifa->ifa_name, options.bind_interface) != 0) 29547dd1d1bSDag-Erling Smørgrav continue; 29647dd1d1bSDag-Erling Smørgrav switch (ifa->ifa_addr->sa_family) { 29747dd1d1bSDag-Erling Smørgrav case AF_INET: 29847dd1d1bSDag-Erling Smørgrav sa = (struct sockaddr_in *)ifa->ifa_addr; 29947dd1d1bSDag-Erling Smørgrav if (!allow_local && sa->sin_addr.s_addr == 30047dd1d1bSDag-Erling Smørgrav htonl(INADDR_LOOPBACK)) 30147dd1d1bSDag-Erling Smørgrav continue; 30247dd1d1bSDag-Erling Smørgrav if (*rlenp < sizeof(struct sockaddr_in)) { 30347dd1d1bSDag-Erling Smørgrav error("%s: v4 addr doesn't fit", 30447dd1d1bSDag-Erling Smørgrav __func__); 30547dd1d1bSDag-Erling Smørgrav return -1; 30647dd1d1bSDag-Erling Smørgrav } 30747dd1d1bSDag-Erling Smørgrav *rlenp = sizeof(struct sockaddr_in); 30847dd1d1bSDag-Erling Smørgrav memcpy(resultp, sa, *rlenp); 30947dd1d1bSDag-Erling Smørgrav return 0; 31047dd1d1bSDag-Erling Smørgrav case AF_INET6: 31147dd1d1bSDag-Erling Smørgrav sa6 = (struct sockaddr_in6 *)ifa->ifa_addr; 31247dd1d1bSDag-Erling Smørgrav v6addr = &sa6->sin6_addr; 31347dd1d1bSDag-Erling Smørgrav if (!allow_local && 31447dd1d1bSDag-Erling Smørgrav (IN6_IS_ADDR_LINKLOCAL(v6addr) || 31547dd1d1bSDag-Erling Smørgrav IN6_IS_ADDR_LOOPBACK(v6addr))) 31647dd1d1bSDag-Erling Smørgrav continue; 31747dd1d1bSDag-Erling Smørgrav if (*rlenp < sizeof(struct sockaddr_in6)) { 31847dd1d1bSDag-Erling Smørgrav error("%s: v6 addr doesn't fit", 31947dd1d1bSDag-Erling Smørgrav __func__); 32047dd1d1bSDag-Erling Smørgrav return -1; 32147dd1d1bSDag-Erling Smørgrav } 32247dd1d1bSDag-Erling Smørgrav *rlenp = sizeof(struct sockaddr_in6); 32347dd1d1bSDag-Erling Smørgrav memcpy(resultp, sa6, *rlenp); 32447dd1d1bSDag-Erling Smørgrav return 0; 32547dd1d1bSDag-Erling Smørgrav } 32647dd1d1bSDag-Erling Smørgrav } 32747dd1d1bSDag-Erling Smørgrav } 32847dd1d1bSDag-Erling Smørgrav return -1; 32947dd1d1bSDag-Erling Smørgrav } 33047dd1d1bSDag-Erling Smørgrav #endif 33147dd1d1bSDag-Erling Smørgrav 332511b41d2SMark Murray /* 333*190cef3dSDag-Erling Smørgrav * Creates a socket for use as the ssh connection. 334511b41d2SMark Murray */ 335af12a3e7SDag-Erling Smørgrav static int 336*190cef3dSDag-Erling Smørgrav ssh_create_socket(struct addrinfo *ai) 337511b41d2SMark Murray { 338*190cef3dSDag-Erling Smørgrav int sock, r; 33947dd1d1bSDag-Erling Smørgrav struct sockaddr_storage bindaddr; 34047dd1d1bSDag-Erling Smørgrav socklen_t bindaddrlen = 0; 341b83788ffSDag-Erling Smørgrav struct addrinfo hints, *res = NULL; 34247dd1d1bSDag-Erling Smørgrav #ifdef HAVE_IFADDRS_H 34347dd1d1bSDag-Erling Smørgrav struct ifaddrs *ifaddrs = NULL; 34447dd1d1bSDag-Erling Smørgrav #endif 34547dd1d1bSDag-Erling Smørgrav char ntop[NI_MAXHOST]; 346511b41d2SMark Murray 347cf2b5f3bSDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 348b15c8340SDag-Erling Smørgrav if (sock < 0) { 349f7167e0eSDag-Erling Smørgrav error("socket: %s", strerror(errno)); 350b15c8340SDag-Erling Smørgrav return -1; 351b15c8340SDag-Erling Smørgrav } 352b15c8340SDag-Erling Smørgrav fcntl(sock, F_SETFD, FD_CLOEXEC); 353af12a3e7SDag-Erling Smørgrav 354af12a3e7SDag-Erling Smørgrav /* Bind the socket to an alternative local IP address */ 355*190cef3dSDag-Erling Smørgrav if (options.bind_address == NULL && options.bind_interface == NULL) 356af12a3e7SDag-Erling Smørgrav return sock; 357af12a3e7SDag-Erling Smørgrav 35847dd1d1bSDag-Erling Smørgrav if (options.bind_address != NULL) { 359af12a3e7SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 360cf2b5f3bSDag-Erling Smørgrav hints.ai_family = ai->ai_family; 361cf2b5f3bSDag-Erling Smørgrav hints.ai_socktype = ai->ai_socktype; 362cf2b5f3bSDag-Erling Smørgrav hints.ai_protocol = ai->ai_protocol; 363af12a3e7SDag-Erling Smørgrav hints.ai_flags = AI_PASSIVE; 36447dd1d1bSDag-Erling Smørgrav if ((r = getaddrinfo(options.bind_address, NULL, 36547dd1d1bSDag-Erling Smørgrav &hints, &res)) != 0) { 366af12a3e7SDag-Erling Smørgrav error("getaddrinfo: %s: %s", options.bind_address, 36747dd1d1bSDag-Erling Smørgrav ssh_gai_strerror(r)); 36847dd1d1bSDag-Erling Smørgrav goto fail; 369511b41d2SMark Murray } 37047dd1d1bSDag-Erling Smørgrav if (res == NULL) { 37147dd1d1bSDag-Erling Smørgrav error("getaddrinfo: no addrs"); 37247dd1d1bSDag-Erling Smørgrav goto fail; 37347dd1d1bSDag-Erling Smørgrav } 37447dd1d1bSDag-Erling Smørgrav if (res->ai_addrlen > sizeof(bindaddr)) { 37547dd1d1bSDag-Erling Smørgrav error("%s: addr doesn't fit", __func__); 37647dd1d1bSDag-Erling Smørgrav goto fail; 37747dd1d1bSDag-Erling Smørgrav } 37847dd1d1bSDag-Erling Smørgrav memcpy(&bindaddr, res->ai_addr, res->ai_addrlen); 37947dd1d1bSDag-Erling Smørgrav bindaddrlen = res->ai_addrlen; 38047dd1d1bSDag-Erling Smørgrav } else if (options.bind_interface != NULL) { 38147dd1d1bSDag-Erling Smørgrav #ifdef HAVE_IFADDRS_H 38247dd1d1bSDag-Erling Smørgrav if ((r = getifaddrs(&ifaddrs)) != 0) { 38347dd1d1bSDag-Erling Smørgrav error("getifaddrs: %s: %s", options.bind_interface, 38447dd1d1bSDag-Erling Smørgrav strerror(errno)); 38547dd1d1bSDag-Erling Smørgrav goto fail; 38647dd1d1bSDag-Erling Smørgrav } 38747dd1d1bSDag-Erling Smørgrav bindaddrlen = sizeof(bindaddr); 38847dd1d1bSDag-Erling Smørgrav if (check_ifaddrs(options.bind_interface, ai->ai_family, 38947dd1d1bSDag-Erling Smørgrav ifaddrs, &bindaddr, &bindaddrlen) != 0) { 39047dd1d1bSDag-Erling Smørgrav logit("getifaddrs: %s: no suitable addresses", 39147dd1d1bSDag-Erling Smørgrav options.bind_interface); 39247dd1d1bSDag-Erling Smørgrav goto fail; 39347dd1d1bSDag-Erling Smørgrav } 39447dd1d1bSDag-Erling Smørgrav #else 39547dd1d1bSDag-Erling Smørgrav error("BindInterface not supported on this platform."); 39647dd1d1bSDag-Erling Smørgrav #endif 39747dd1d1bSDag-Erling Smørgrav } 39847dd1d1bSDag-Erling Smørgrav if ((r = getnameinfo((struct sockaddr *)&bindaddr, bindaddrlen, 39947dd1d1bSDag-Erling Smørgrav ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST)) != 0) { 40047dd1d1bSDag-Erling Smørgrav error("%s: getnameinfo failed: %s", __func__, 40147dd1d1bSDag-Erling Smørgrav ssh_gai_strerror(r)); 40247dd1d1bSDag-Erling Smørgrav goto fail; 403b83788ffSDag-Erling Smørgrav } 404*190cef3dSDag-Erling Smørgrav if (bind(sock, (struct sockaddr *)&bindaddr, bindaddrlen) != 0) { 40547dd1d1bSDag-Erling Smørgrav error("bind %s: %s", ntop, strerror(errno)); 40647dd1d1bSDag-Erling Smørgrav goto fail; 40747dd1d1bSDag-Erling Smørgrav } 40847dd1d1bSDag-Erling Smørgrav debug("%s: bound to %s", __func__, ntop); 40947dd1d1bSDag-Erling Smørgrav /* success */ 41047dd1d1bSDag-Erling Smørgrav goto out; 411f7167e0eSDag-Erling Smørgrav fail: 412af12a3e7SDag-Erling Smørgrav close(sock); 41347dd1d1bSDag-Erling Smørgrav sock = -1; 41447dd1d1bSDag-Erling Smørgrav out: 415b83788ffSDag-Erling Smørgrav if (res != NULL) 416af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 41747dd1d1bSDag-Erling Smørgrav #ifdef HAVE_IFADDRS_H 41847dd1d1bSDag-Erling Smørgrav if (ifaddrs != NULL) 41947dd1d1bSDag-Erling Smørgrav freeifaddrs(ifaddrs); 42047dd1d1bSDag-Erling Smørgrav #endif 421511b41d2SMark Murray return sock; 422511b41d2SMark Murray } 423511b41d2SMark Murray 4244f52dfbbSDag-Erling Smørgrav /* 4254f52dfbbSDag-Erling Smørgrav * Wait up to *timeoutp milliseconds for fd to be readable. Updates 4264f52dfbbSDag-Erling Smørgrav * *timeoutp with time remaining. 4274f52dfbbSDag-Erling Smørgrav * Returns 0 if fd ready or -1 on timeout or error (see errno). 4284f52dfbbSDag-Erling Smørgrav */ 4294f52dfbbSDag-Erling Smørgrav static int 4304f52dfbbSDag-Erling Smørgrav waitrfd(int fd, int *timeoutp) 4314f52dfbbSDag-Erling Smørgrav { 4324f52dfbbSDag-Erling Smørgrav struct pollfd pfd; 4334f52dfbbSDag-Erling Smørgrav struct timeval t_start; 4344f52dfbbSDag-Erling Smørgrav int oerrno, r; 4354f52dfbbSDag-Erling Smørgrav 43647dd1d1bSDag-Erling Smørgrav monotime_tv(&t_start); 4374f52dfbbSDag-Erling Smørgrav pfd.fd = fd; 4384f52dfbbSDag-Erling Smørgrav pfd.events = POLLIN; 4394f52dfbbSDag-Erling Smørgrav for (; *timeoutp >= 0;) { 4404f52dfbbSDag-Erling Smørgrav r = poll(&pfd, 1, *timeoutp); 4414f52dfbbSDag-Erling Smørgrav oerrno = errno; 4424f52dfbbSDag-Erling Smørgrav ms_subtract_diff(&t_start, timeoutp); 4434f52dfbbSDag-Erling Smørgrav errno = oerrno; 4444f52dfbbSDag-Erling Smørgrav if (r > 0) 4454f52dfbbSDag-Erling Smørgrav return 0; 4464f52dfbbSDag-Erling Smørgrav else if (r == -1 && errno != EAGAIN) 4474f52dfbbSDag-Erling Smørgrav return -1; 4484f52dfbbSDag-Erling Smørgrav else if (r == 0) 4494f52dfbbSDag-Erling Smørgrav break; 4504f52dfbbSDag-Erling Smørgrav } 4514f52dfbbSDag-Erling Smørgrav /* timeout */ 4524f52dfbbSDag-Erling Smørgrav errno = ETIMEDOUT; 4534f52dfbbSDag-Erling Smørgrav return -1; 4544f52dfbbSDag-Erling Smørgrav } 4554f52dfbbSDag-Erling Smørgrav 456cf2b5f3bSDag-Erling Smørgrav static int 457cf2b5f3bSDag-Erling Smørgrav timeout_connect(int sockfd, const struct sockaddr *serv_addr, 458d4af9e69SDag-Erling Smørgrav socklen_t addrlen, int *timeoutp) 459cf2b5f3bSDag-Erling Smørgrav { 4604f52dfbbSDag-Erling Smørgrav int optval = 0; 4614f52dfbbSDag-Erling Smørgrav socklen_t optlen = sizeof(optval); 462cf2b5f3bSDag-Erling Smørgrav 4634f52dfbbSDag-Erling Smørgrav /* No timeout: just do a blocking connect() */ 4644f52dfbbSDag-Erling Smørgrav if (*timeoutp <= 0) 4654f52dfbbSDag-Erling Smørgrav return connect(sockfd, serv_addr, addrlen); 466cf2b5f3bSDag-Erling Smørgrav 4671ec0d754SDag-Erling Smørgrav set_nonblock(sockfd); 4684f52dfbbSDag-Erling Smørgrav if (connect(sockfd, serv_addr, addrlen) == 0) { 4694f52dfbbSDag-Erling Smørgrav /* Succeeded already? */ 4701ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 4714f52dfbbSDag-Erling Smørgrav return 0; 4724f52dfbbSDag-Erling Smørgrav } else if (errno != EINPROGRESS) 4734f52dfbbSDag-Erling Smørgrav return -1; 474cf2b5f3bSDag-Erling Smørgrav 4754f52dfbbSDag-Erling Smørgrav if (waitrfd(sockfd, timeoutp) == -1) 4764f52dfbbSDag-Erling Smørgrav return -1; 477cf2b5f3bSDag-Erling Smørgrav 478cf2b5f3bSDag-Erling Smørgrav /* Completed or failed */ 4794f52dfbbSDag-Erling Smørgrav if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { 480cf2b5f3bSDag-Erling Smørgrav debug("getsockopt: %s", strerror(errno)); 4814f52dfbbSDag-Erling Smørgrav return -1; 482cf2b5f3bSDag-Erling Smørgrav } 483cf2b5f3bSDag-Erling Smørgrav if (optval != 0) { 484cf2b5f3bSDag-Erling Smørgrav errno = optval; 4854f52dfbbSDag-Erling Smørgrav return -1; 486cf2b5f3bSDag-Erling Smørgrav } 4871ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 4884f52dfbbSDag-Erling Smørgrav return 0; 489cf2b5f3bSDag-Erling Smørgrav } 490cf2b5f3bSDag-Erling Smørgrav 491511b41d2SMark Murray /* 492511b41d2SMark Murray * Opens a TCP/IP connection to the remote server on the given host. 493511b41d2SMark Murray * The address of the remote host will be returned in hostaddr. 494*190cef3dSDag-Erling Smørgrav * If port is 0, the default port will be used. 495511b41d2SMark Murray * Connection_attempts specifies the maximum number of tries (one per 496511b41d2SMark Murray * second). If proxy_command is non-NULL, it specifies the command (with %h 497511b41d2SMark Murray * and %p substituted for host and port, respectively) to use to contact 498511b41d2SMark Murray * the daemon. 499511b41d2SMark Murray */ 500f7167e0eSDag-Erling Smørgrav static int 5014f52dfbbSDag-Erling Smørgrav ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop, 502f7167e0eSDag-Erling Smørgrav struct sockaddr_storage *hostaddr, u_short port, int family, 503*190cef3dSDag-Erling Smørgrav int connection_attempts, int *timeout_ms, int want_keepalive) 504511b41d2SMark Murray { 505ca3176e7SBrian Feldman int on = 1; 50647dd1d1bSDag-Erling Smørgrav int oerrno, sock = -1, attempt; 507ca3176e7SBrian Feldman char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 508f7167e0eSDag-Erling Smørgrav struct addrinfo *ai; 509511b41d2SMark Murray 510*190cef3dSDag-Erling Smørgrav debug2("%s", __func__); 511acc1a9efSDag-Erling Smørgrav memset(ntop, 0, sizeof(ntop)); 512acc1a9efSDag-Erling Smørgrav memset(strport, 0, sizeof(strport)); 513511b41d2SMark Murray 514333ee039SDag-Erling Smørgrav for (attempt = 0; attempt < connection_attempts; attempt++) { 51562efe23aSDag-Erling Smørgrav if (attempt > 0) { 51662efe23aSDag-Erling Smørgrav /* Sleep a moment before retrying. */ 51762efe23aSDag-Erling Smørgrav sleep(1); 518511b41d2SMark Murray debug("Trying again..."); 51962efe23aSDag-Erling Smørgrav } 520333ee039SDag-Erling Smørgrav /* 521333ee039SDag-Erling Smørgrav * Loop through addresses for this host, and try each one in 522333ee039SDag-Erling Smørgrav * sequence until the connection succeeds. 523333ee039SDag-Erling Smørgrav */ 524511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 525f7167e0eSDag-Erling Smørgrav if (ai->ai_family != AF_INET && 52647dd1d1bSDag-Erling Smørgrav ai->ai_family != AF_INET6) { 52747dd1d1bSDag-Erling Smørgrav errno = EAFNOSUPPORT; 528511b41d2SMark Murray continue; 52947dd1d1bSDag-Erling Smørgrav } 530511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 531511b41d2SMark Murray ntop, sizeof(ntop), strport, sizeof(strport), 532511b41d2SMark Murray NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 53347dd1d1bSDag-Erling Smørgrav oerrno = errno; 534acc1a9efSDag-Erling Smørgrav error("%s: getnameinfo failed", __func__); 53547dd1d1bSDag-Erling Smørgrav errno = oerrno; 536511b41d2SMark Murray continue; 537511b41d2SMark Murray } 538511b41d2SMark Murray debug("Connecting to %.200s [%.100s] port %s.", 5391f5ce8f4SBrian Feldman host, ntop, strport); 540511b41d2SMark Murray 541511b41d2SMark Murray /* Create a socket for connecting. */ 542*190cef3dSDag-Erling Smørgrav sock = ssh_create_socket(ai); 54347dd1d1bSDag-Erling Smørgrav if (sock < 0) { 544af12a3e7SDag-Erling Smørgrav /* Any error is already output */ 54547dd1d1bSDag-Erling Smørgrav errno = 0; 546511b41d2SMark Murray continue; 54747dd1d1bSDag-Erling Smørgrav } 548511b41d2SMark Murray 549cf2b5f3bSDag-Erling Smørgrav if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 550d4af9e69SDag-Erling Smørgrav timeout_ms) >= 0) { 551511b41d2SMark Murray /* Successful connection. */ 552c322fe35SKris Kennaway memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 553511b41d2SMark Murray break; 554511b41d2SMark Murray } else { 55547dd1d1bSDag-Erling Smørgrav oerrno = errno; 556f388f5efSDag-Erling Smørgrav debug("connect to address %s port %s: %s", 557f388f5efSDag-Erling Smørgrav ntop, strport, strerror(errno)); 558511b41d2SMark Murray close(sock); 559333ee039SDag-Erling Smørgrav sock = -1; 56047dd1d1bSDag-Erling Smørgrav errno = oerrno; 561511b41d2SMark Murray } 562511b41d2SMark Murray } 563333ee039SDag-Erling Smørgrav if (sock != -1) 564511b41d2SMark Murray break; /* Successful connection. */ 565511b41d2SMark Murray } 566511b41d2SMark Murray 567511b41d2SMark Murray /* Return failure if we didn't get a successful connection. */ 568333ee039SDag-Erling Smørgrav if (sock == -1) { 569aa49c926SDag-Erling Smørgrav error("ssh: connect to host %s port %s: %s", 57047dd1d1bSDag-Erling Smørgrav host, strport, errno == 0 ? "failure" : strerror(errno)); 57147dd1d1bSDag-Erling Smørgrav return -1; 572f388f5efSDag-Erling Smørgrav } 573511b41d2SMark Murray 574511b41d2SMark Murray debug("Connection established."); 575511b41d2SMark Murray 5761ec0d754SDag-Erling Smørgrav /* Set SO_KEEPALIVE if requested. */ 577d4af9e69SDag-Erling Smørgrav if (want_keepalive && 578ca3176e7SBrian Feldman setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 579ca3176e7SBrian Feldman sizeof(on)) < 0) 580ca3176e7SBrian Feldman error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 581ca3176e7SBrian Feldman 582511b41d2SMark Murray /* Set the connection. */ 5834f52dfbbSDag-Erling Smørgrav if (ssh_packet_set_connection(ssh, sock, sock) == NULL) 5844f52dfbbSDag-Erling Smørgrav return -1; /* ssh_packet_set_connection logs error */ 585511b41d2SMark Murray 586af12a3e7SDag-Erling Smørgrav return 0; 587511b41d2SMark Murray } 588511b41d2SMark Murray 589f7167e0eSDag-Erling Smørgrav int 5904f52dfbbSDag-Erling Smørgrav ssh_connect(struct ssh *ssh, const char *host, struct addrinfo *addrs, 591f7167e0eSDag-Erling Smørgrav struct sockaddr_storage *hostaddr, u_short port, int family, 592*190cef3dSDag-Erling Smørgrav int connection_attempts, int *timeout_ms, int want_keepalive) 593f7167e0eSDag-Erling Smørgrav { 594f7167e0eSDag-Erling Smørgrav if (options.proxy_command == NULL) { 5954f52dfbbSDag-Erling Smørgrav return ssh_connect_direct(ssh, host, addrs, hostaddr, port, 596*190cef3dSDag-Erling Smørgrav family, connection_attempts, timeout_ms, want_keepalive); 597f7167e0eSDag-Erling Smørgrav } else if (strcmp(options.proxy_command, "-") == 0) { 5984f52dfbbSDag-Erling Smørgrav if ((ssh_packet_set_connection(ssh, 5994f52dfbbSDag-Erling Smørgrav STDIN_FILENO, STDOUT_FILENO)) == NULL) 6004f52dfbbSDag-Erling Smørgrav return -1; /* ssh_packet_set_connection logs error */ 6014f52dfbbSDag-Erling Smørgrav return 0; 602f7167e0eSDag-Erling Smørgrav } else if (options.proxy_use_fdpass) { 6034f52dfbbSDag-Erling Smørgrav return ssh_proxy_fdpass_connect(ssh, host, port, 604f7167e0eSDag-Erling Smørgrav options.proxy_command); 605f7167e0eSDag-Erling Smørgrav } 6064f52dfbbSDag-Erling Smørgrav return ssh_proxy_connect(ssh, host, port, options.proxy_command); 607f7167e0eSDag-Erling Smørgrav } 608f7167e0eSDag-Erling Smørgrav 6096888a9beSDag-Erling Smørgrav static void 6106888a9beSDag-Erling Smørgrav send_client_banner(int connection_out, int minor1) 6116888a9beSDag-Erling Smørgrav { 6126888a9beSDag-Erling Smørgrav /* Send our own protocol version identification. */ 6134f52dfbbSDag-Erling Smørgrav xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s\n", 6144f52dfbbSDag-Erling Smørgrav PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, 6156888a9beSDag-Erling Smørgrav *options.version_addendum == '\0' ? "" : " ", 6164f52dfbbSDag-Erling Smørgrav options.version_addendum); 617acc1a9efSDag-Erling Smørgrav if (atomicio(vwrite, connection_out, client_version_string, 6186888a9beSDag-Erling Smørgrav strlen(client_version_string)) != strlen(client_version_string)) 6196888a9beSDag-Erling Smørgrav fatal("write: %.100s", strerror(errno)); 6206888a9beSDag-Erling Smørgrav chop(client_version_string); 6216888a9beSDag-Erling Smørgrav debug("Local version string %.100s", client_version_string); 6226888a9beSDag-Erling Smørgrav } 6236888a9beSDag-Erling Smørgrav 624511b41d2SMark Murray /* 625e8aafc91SKris Kennaway * Waits for the server identification string, and sends our own 626e8aafc91SKris Kennaway * identification string. 627511b41d2SMark Murray */ 6287aee6ffeSDag-Erling Smørgrav void 629d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(int timeout_ms) 630511b41d2SMark Murray { 631e8aafc91SKris Kennaway char buf[256], remote_version[256]; /* must be same size! */ 632d4ecd108SDag-Erling Smørgrav int remote_major, remote_minor, mismatch; 633e8aafc91SKris Kennaway int connection_in = packet_get_connection_in(); 634e8aafc91SKris Kennaway int connection_out = packet_get_connection_out(); 635333ee039SDag-Erling Smørgrav u_int i, n; 636d4af9e69SDag-Erling Smørgrav size_t len; 6374f52dfbbSDag-Erling Smørgrav int rc; 638d4af9e69SDag-Erling Smørgrav 6396888a9beSDag-Erling Smørgrav send_client_banner(connection_out, 0); 6406888a9beSDag-Erling Smørgrav 641d4ecd108SDag-Erling Smørgrav /* Read other side's version identification. */ 642333ee039SDag-Erling Smørgrav for (n = 0;;) { 643e8aafc91SKris Kennaway for (i = 0; i < sizeof(buf) - 1; i++) { 644d4af9e69SDag-Erling Smørgrav if (timeout_ms > 0) { 6454f52dfbbSDag-Erling Smørgrav rc = waitrfd(connection_in, &timeout_ms); 6464f52dfbbSDag-Erling Smørgrav if (rc == -1 && errno == ETIMEDOUT) { 647d4af9e69SDag-Erling Smørgrav fatal("Connection timed out during " 648d4af9e69SDag-Erling Smørgrav "banner exchange"); 6494f52dfbbSDag-Erling Smørgrav } else if (rc == -1) { 6504f52dfbbSDag-Erling Smørgrav fatal("%s: %s", 6514f52dfbbSDag-Erling Smørgrav __func__, strerror(errno)); 652d4af9e69SDag-Erling Smørgrav } 653d4af9e69SDag-Erling Smørgrav } 654d4af9e69SDag-Erling Smørgrav 655acc1a9efSDag-Erling Smørgrav len = atomicio(read, connection_in, &buf[i], 1); 656d4ecd108SDag-Erling Smørgrav if (len != 1 && errno == EPIPE) 657d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 658d4af9e69SDag-Erling Smørgrav "Connection closed by remote host"); 659d4ecd108SDag-Erling Smørgrav else if (len != 1) 660d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 661d4af9e69SDag-Erling Smørgrav "read: %.100s", strerror(errno)); 662e8aafc91SKris Kennaway if (buf[i] == '\r') { 663e8aafc91SKris Kennaway buf[i] = '\n'; 664e8aafc91SKris Kennaway buf[i + 1] = 0; 665e8aafc91SKris Kennaway continue; /**XXX wait for \n */ 666511b41d2SMark Murray } 667e8aafc91SKris Kennaway if (buf[i] == '\n') { 668e8aafc91SKris Kennaway buf[i + 1] = 0; 669511b41d2SMark Murray break; 670e8aafc91SKris Kennaway } 671333ee039SDag-Erling Smørgrav if (++n > 65536) 672d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 673d4af9e69SDag-Erling Smørgrav "No banner received"); 674e8aafc91SKris Kennaway } 675e8aafc91SKris Kennaway buf[sizeof(buf) - 1] = 0; 676c2d3a559SKris Kennaway if (strncmp(buf, "SSH-", 4) == 0) 677c2d3a559SKris Kennaway break; 678c2d3a559SKris Kennaway debug("ssh_exchange_identification: %s", buf); 679c2d3a559SKris Kennaway } 680e8aafc91SKris Kennaway server_version_string = xstrdup(buf); 681511b41d2SMark Murray 682511b41d2SMark Murray /* 683e8aafc91SKris Kennaway * Check that the versions match. In future this might accept 684e8aafc91SKris Kennaway * several versions and set appropriate flags to handle them. 685511b41d2SMark Murray */ 686e8aafc91SKris Kennaway if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 687e8aafc91SKris Kennaway &remote_major, &remote_minor, remote_version) != 3) 688e8aafc91SKris Kennaway fatal("Bad remote protocol version identification: '%.100s'", buf); 689e8aafc91SKris Kennaway debug("Remote protocol version %d.%d, remote software version %.100s", 690e8aafc91SKris Kennaway remote_major, remote_minor, remote_version); 691511b41d2SMark Murray 692bc5531deSDag-Erling Smørgrav active_state->compat = compat_datafellows(remote_version); 693e8aafc91SKris Kennaway mismatch = 0; 694e8aafc91SKris Kennaway 695e8aafc91SKris Kennaway switch (remote_major) { 6964f52dfbbSDag-Erling Smørgrav case 2: 697511b41d2SMark Murray break; 6984f52dfbbSDag-Erling Smørgrav case 1: 6994f52dfbbSDag-Erling Smørgrav if (remote_minor != 99) 700e8aafc91SKris Kennaway mismatch = 1; 701e8aafc91SKris Kennaway break; 702511b41d2SMark Murray default: 703e8aafc91SKris Kennaway mismatch = 1; 704e8aafc91SKris Kennaway break; 705511b41d2SMark Murray } 706e8aafc91SKris Kennaway if (mismatch) 707e8aafc91SKris Kennaway fatal("Protocol major versions differ: %d vs. %d", 7084f52dfbbSDag-Erling Smørgrav PROTOCOL_MAJOR_2, remote_major); 709f7167e0eSDag-Erling Smørgrav if ((datafellows & SSH_BUG_RSASIGMD5) != 0) 710f7167e0eSDag-Erling Smørgrav logit("Server version \"%.100s\" uses unsafe RSA signature " 711f7167e0eSDag-Erling Smørgrav "scheme; disabling use of RSA keys", remote_version); 712e8aafc91SKris Kennaway chop(server_version_string); 713511b41d2SMark Murray } 714511b41d2SMark Murray 715ca3176e7SBrian Feldman /* defaults to 'no' */ 716af12a3e7SDag-Erling Smørgrav static int 717af12a3e7SDag-Erling Smørgrav confirm(const char *prompt) 718511b41d2SMark Murray { 719af12a3e7SDag-Erling Smørgrav const char *msg, *again = "Please type 'yes' or 'no': "; 720af12a3e7SDag-Erling Smørgrav char *p; 721af12a3e7SDag-Erling Smørgrav int ret = -1; 722511b41d2SMark Murray 723ca3176e7SBrian Feldman if (options.batch_mode) 724ca3176e7SBrian Feldman return 0; 725af12a3e7SDag-Erling Smørgrav for (msg = prompt;;msg = again) { 726af12a3e7SDag-Erling Smørgrav p = read_passphrase(msg, RP_ECHO); 72747dd1d1bSDag-Erling Smørgrav if (p == NULL) 72847dd1d1bSDag-Erling Smørgrav return 0; 72947dd1d1bSDag-Erling Smørgrav p[strcspn(p, "\n")] = '\0'; 73047dd1d1bSDag-Erling Smørgrav if (p[0] == '\0' || strcasecmp(p, "no") == 0) 731af12a3e7SDag-Erling Smørgrav ret = 0; 73247dd1d1bSDag-Erling Smørgrav else if (strcasecmp(p, "yes") == 0) 733af12a3e7SDag-Erling Smørgrav ret = 1; 734e4a9863fSDag-Erling Smørgrav free(p); 735af12a3e7SDag-Erling Smørgrav if (ret != -1) 736af12a3e7SDag-Erling Smørgrav return ret; 737511b41d2SMark Murray } 738511b41d2SMark Murray } 739511b41d2SMark Murray 740b15c8340SDag-Erling Smørgrav static int 7414f52dfbbSDag-Erling Smørgrav check_host_cert(const char *host, const struct sshkey *host_key) 742b15c8340SDag-Erling Smørgrav { 743b15c8340SDag-Erling Smørgrav const char *reason; 744b15c8340SDag-Erling Smørgrav 745*190cef3dSDag-Erling Smørgrav if (sshkey_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { 746b15c8340SDag-Erling Smørgrav error("%s", reason); 747b15c8340SDag-Erling Smørgrav return 0; 748b15c8340SDag-Erling Smørgrav } 749*190cef3dSDag-Erling Smørgrav if (sshbuf_len(host_key->cert->critical) != 0) { 750e2f6069cSDag-Erling Smørgrav error("Certificate for %s contains unsupported " 751e2f6069cSDag-Erling Smørgrav "critical options(s)", host); 752b15c8340SDag-Erling Smørgrav return 0; 753b15c8340SDag-Erling Smørgrav } 754b15c8340SDag-Erling Smørgrav return 1; 755b15c8340SDag-Erling Smørgrav } 756b15c8340SDag-Erling Smørgrav 7574a421b63SDag-Erling Smørgrav static int 7584a421b63SDag-Erling Smørgrav sockaddr_is_local(struct sockaddr *hostaddr) 7594a421b63SDag-Erling Smørgrav { 7604a421b63SDag-Erling Smørgrav switch (hostaddr->sa_family) { 7614a421b63SDag-Erling Smørgrav case AF_INET: 7624a421b63SDag-Erling Smørgrav return (ntohl(((struct sockaddr_in *)hostaddr)-> 7634a421b63SDag-Erling Smørgrav sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 7644a421b63SDag-Erling Smørgrav case AF_INET6: 7654a421b63SDag-Erling Smørgrav return IN6_IS_ADDR_LOOPBACK( 7664a421b63SDag-Erling Smørgrav &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 7674a421b63SDag-Erling Smørgrav default: 7684a421b63SDag-Erling Smørgrav return 0; 7694a421b63SDag-Erling Smørgrav } 7704a421b63SDag-Erling Smørgrav } 7714a421b63SDag-Erling Smørgrav 7724a421b63SDag-Erling Smørgrav /* 7734a421b63SDag-Erling Smørgrav * Prepare the hostname and ip address strings that are used to lookup 7744a421b63SDag-Erling Smørgrav * host keys in known_hosts files. These may have a port number appended. 7754a421b63SDag-Erling Smørgrav */ 7764a421b63SDag-Erling Smørgrav void 7774a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, 7784a421b63SDag-Erling Smørgrav u_short port, char **hostfile_hostname, char **hostfile_ipaddr) 7794a421b63SDag-Erling Smørgrav { 7804a421b63SDag-Erling Smørgrav char ntop[NI_MAXHOST]; 7814a421b63SDag-Erling Smørgrav socklen_t addrlen; 7824a421b63SDag-Erling Smørgrav 7834a421b63SDag-Erling Smørgrav switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { 7844a421b63SDag-Erling Smørgrav case -1: 7854a421b63SDag-Erling Smørgrav addrlen = 0; 7864a421b63SDag-Erling Smørgrav break; 7874a421b63SDag-Erling Smørgrav case AF_INET: 7884a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr_in); 7894a421b63SDag-Erling Smørgrav break; 7904a421b63SDag-Erling Smørgrav case AF_INET6: 7914a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr_in6); 7924a421b63SDag-Erling Smørgrav break; 7934a421b63SDag-Erling Smørgrav default: 7944a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr); 7954a421b63SDag-Erling Smørgrav break; 7964a421b63SDag-Erling Smørgrav } 7974a421b63SDag-Erling Smørgrav 7984a421b63SDag-Erling Smørgrav /* 7994a421b63SDag-Erling Smørgrav * We don't have the remote ip-address for connections 8004a421b63SDag-Erling Smørgrav * using a proxy command 8014a421b63SDag-Erling Smørgrav */ 8024a421b63SDag-Erling Smørgrav if (hostfile_ipaddr != NULL) { 8034a421b63SDag-Erling Smørgrav if (options.proxy_command == NULL) { 8044a421b63SDag-Erling Smørgrav if (getnameinfo(hostaddr, addrlen, 8054a421b63SDag-Erling Smørgrav ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) 806bc5531deSDag-Erling Smørgrav fatal("%s: getnameinfo failed", __func__); 8074a421b63SDag-Erling Smørgrav *hostfile_ipaddr = put_host_port(ntop, port); 8084a421b63SDag-Erling Smørgrav } else { 8094a421b63SDag-Erling Smørgrav *hostfile_ipaddr = xstrdup("<no hostip for proxy " 8104a421b63SDag-Erling Smørgrav "command>"); 8114a421b63SDag-Erling Smørgrav } 8124a421b63SDag-Erling Smørgrav } 8134a421b63SDag-Erling Smørgrav 8144a421b63SDag-Erling Smørgrav /* 8154a421b63SDag-Erling Smørgrav * Allow the user to record the key under a different name or 8164a421b63SDag-Erling Smørgrav * differentiate a non-standard port. This is useful for ssh 8174a421b63SDag-Erling Smørgrav * tunneling over forwarded connections or if you run multiple 8184a421b63SDag-Erling Smørgrav * sshd's on different ports on the same machine. 8194a421b63SDag-Erling Smørgrav */ 8204a421b63SDag-Erling Smørgrav if (hostfile_hostname != NULL) { 8214a421b63SDag-Erling Smørgrav if (options.host_key_alias != NULL) { 8224a421b63SDag-Erling Smørgrav *hostfile_hostname = xstrdup(options.host_key_alias); 8234a421b63SDag-Erling Smørgrav debug("using hostkeyalias: %s", *hostfile_hostname); 8244a421b63SDag-Erling Smørgrav } else { 8254a421b63SDag-Erling Smørgrav *hostfile_hostname = put_host_port(hostname, port); 8264a421b63SDag-Erling Smørgrav } 8274a421b63SDag-Erling Smørgrav } 8284a421b63SDag-Erling Smørgrav } 8294a421b63SDag-Erling Smørgrav 830e8aafc91SKris Kennaway /* 831af12a3e7SDag-Erling Smørgrav * check whether the supplied host key is valid, return -1 if the key 832e146993eSDag-Erling Smørgrav * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. 833e8aafc91SKris Kennaway */ 834333ee039SDag-Erling Smørgrav #define RDRW 0 835333ee039SDag-Erling Smørgrav #define RDONLY 1 836333ee039SDag-Erling Smørgrav #define ROQUIET 2 837af12a3e7SDag-Erling Smørgrav static int 838333ee039SDag-Erling Smørgrav check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 8394f52dfbbSDag-Erling Smørgrav struct sshkey *host_key, int readonly, 840e146993eSDag-Erling Smørgrav char **user_hostfiles, u_int num_user_hostfiles, 841e146993eSDag-Erling Smørgrav char **system_hostfiles, u_int num_system_hostfiles) 842511b41d2SMark Murray { 843e8aafc91SKris Kennaway HostStatus host_status; 844e8aafc91SKris Kennaway HostStatus ip_status; 8454f52dfbbSDag-Erling Smørgrav struct sshkey *raw_key = NULL; 846e146993eSDag-Erling Smørgrav char *ip = NULL, *host = NULL; 847e146993eSDag-Erling Smørgrav char hostline[1000], *hostp, *fp, *ra; 848af12a3e7SDag-Erling Smørgrav char msg[1024]; 849e146993eSDag-Erling Smørgrav const char *type; 8504a421b63SDag-Erling Smørgrav const struct hostkey_entry *host_found, *ip_found; 851e146993eSDag-Erling Smørgrav int len, cancelled_forwarding = 0; 852e146993eSDag-Erling Smørgrav int local = sockaddr_is_local(hostaddr); 8534f52dfbbSDag-Erling Smørgrav int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0; 854bc5531deSDag-Erling Smørgrav int hostkey_trusted = 0; /* Known or explicitly accepted by user */ 855e146993eSDag-Erling Smørgrav struct hostkeys *host_hostkeys, *ip_hostkeys; 856e146993eSDag-Erling Smørgrav u_int i; 857511b41d2SMark Murray 858e8aafc91SKris Kennaway /* 859e8aafc91SKris Kennaway * Force accepting of the host key for loopback/localhost. The 860e8aafc91SKris Kennaway * problem is that if the home directory is NFS-mounted to multiple 861e8aafc91SKris Kennaway * machines, localhost will refer to a different machine in each of 862e8aafc91SKris Kennaway * them, and the user will get bogus HOST_CHANGED warnings. This 863e8aafc91SKris Kennaway * essentially disables host authentication for localhost; however, 864e8aafc91SKris Kennaway * this is probably not a real problem. 865e8aafc91SKris Kennaway */ 866af12a3e7SDag-Erling Smørgrav if (options.no_host_authentication_for_localhost == 1 && local && 867af12a3e7SDag-Erling Smørgrav options.host_key_alias == NULL) { 868ca3176e7SBrian Feldman debug("Forcing accepting of host key for " 869ca3176e7SBrian Feldman "loopback/localhost."); 870af12a3e7SDag-Erling Smørgrav return 0; 871511b41d2SMark Murray } 872511b41d2SMark Murray 873e8aafc91SKris Kennaway /* 8744a421b63SDag-Erling Smørgrav * Prepare the hostname and address strings used for hostkey lookup. 8754a421b63SDag-Erling Smørgrav * In some cases, these will have a port number appended. 876e8aafc91SKris Kennaway */ 8774a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); 878d4af9e69SDag-Erling Smørgrav 879ca3176e7SBrian Feldman /* 880ca3176e7SBrian Feldman * Turn off check_host_ip if the connection is to localhost, via proxy 881ca3176e7SBrian Feldman * command or if we don't have a hostname to compare with 882ca3176e7SBrian Feldman */ 883333ee039SDag-Erling Smørgrav if (options.check_host_ip && (local || 884333ee039SDag-Erling Smørgrav strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 885ca3176e7SBrian Feldman options.check_host_ip = 0; 886ca3176e7SBrian Feldman 8874a421b63SDag-Erling Smørgrav host_hostkeys = init_hostkeys(); 888e146993eSDag-Erling Smørgrav for (i = 0; i < num_user_hostfiles; i++) 889e146993eSDag-Erling Smørgrav load_hostkeys(host_hostkeys, host, user_hostfiles[i]); 890e146993eSDag-Erling Smørgrav for (i = 0; i < num_system_hostfiles; i++) 891e146993eSDag-Erling Smørgrav load_hostkeys(host_hostkeys, host, system_hostfiles[i]); 8924a421b63SDag-Erling Smørgrav 8934a421b63SDag-Erling Smørgrav ip_hostkeys = NULL; 8944a421b63SDag-Erling Smørgrav if (!want_cert && options.check_host_ip) { 8954a421b63SDag-Erling Smørgrav ip_hostkeys = init_hostkeys(); 896e146993eSDag-Erling Smørgrav for (i = 0; i < num_user_hostfiles; i++) 897e146993eSDag-Erling Smørgrav load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]); 898e146993eSDag-Erling Smørgrav for (i = 0; i < num_system_hostfiles; i++) 899e146993eSDag-Erling Smørgrav load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]); 900e8aafc91SKris Kennaway } 901e8aafc91SKris Kennaway 902b15c8340SDag-Erling Smørgrav retry: 9034a421b63SDag-Erling Smørgrav /* Reload these as they may have changed on cert->key downgrade */ 9044f52dfbbSDag-Erling Smørgrav want_cert = sshkey_is_cert(host_key); 9054f52dfbbSDag-Erling Smørgrav type = sshkey_type(host_key); 906b15c8340SDag-Erling Smørgrav 907e8aafc91SKris Kennaway /* 908b74df5b2SDag-Erling Smørgrav * Check if the host key is present in the user's list of known 909e8aafc91SKris Kennaway * hosts or in the systemwide list. 910e8aafc91SKris Kennaway */ 9114a421b63SDag-Erling Smørgrav host_status = check_key_in_hostkeys(host_hostkeys, host_key, 9124a421b63SDag-Erling Smørgrav &host_found); 9134a421b63SDag-Erling Smørgrav 914e8aafc91SKris Kennaway /* 915e8aafc91SKris Kennaway * Also perform check for the ip address, skip the check if we are 916b15c8340SDag-Erling Smørgrav * localhost, looking for a certificate, or the hostname was an ip 917b15c8340SDag-Erling Smørgrav * address to begin with. 918e8aafc91SKris Kennaway */ 9194a421b63SDag-Erling Smørgrav if (!want_cert && ip_hostkeys != NULL) { 9204a421b63SDag-Erling Smørgrav ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, 9214a421b63SDag-Erling Smørgrav &ip_found); 922e8aafc91SKris Kennaway if (host_status == HOST_CHANGED && 9234a421b63SDag-Erling Smørgrav (ip_status != HOST_CHANGED || 9244a421b63SDag-Erling Smørgrav (ip_found != NULL && 9254f52dfbbSDag-Erling Smørgrav !sshkey_equal(ip_found->key, host_found->key)))) 926e8aafc91SKris Kennaway host_ip_differ = 1; 927e8aafc91SKris Kennaway } else 928e8aafc91SKris Kennaway ip_status = host_status; 929e8aafc91SKris Kennaway 930e8aafc91SKris Kennaway switch (host_status) { 931e8aafc91SKris Kennaway case HOST_OK: 932e8aafc91SKris Kennaway /* The host is known and the key matches. */ 933b15c8340SDag-Erling Smørgrav debug("Host '%.200s' is known and matches the %s host %s.", 934b15c8340SDag-Erling Smørgrav host, type, want_cert ? "certificate" : "key"); 9354a421b63SDag-Erling Smørgrav debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", 9364a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 9374f52dfbbSDag-Erling Smørgrav if (want_cert && 9384f52dfbbSDag-Erling Smørgrav !check_host_cert(options.host_key_alias == NULL ? 9394f52dfbbSDag-Erling Smørgrav hostname : options.host_key_alias, host_key)) 940b15c8340SDag-Erling Smørgrav goto fail; 941ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 942b15c8340SDag-Erling Smørgrav if (readonly || want_cert) 943cf2b5f3bSDag-Erling Smørgrav logit("%s host key for IP address " 944af12a3e7SDag-Erling Smørgrav "'%.128s' not in list of known hosts.", 945e8aafc91SKris Kennaway type, ip); 946e146993eSDag-Erling Smørgrav else if (!add_host_to_hostfile(user_hostfiles[0], ip, 947aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts)) 948cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the %s host key for IP " 949af12a3e7SDag-Erling Smørgrav "address '%.128s' to the list of known " 950557f75e5SDag-Erling Smørgrav "hosts (%.500s).", type, ip, 951e146993eSDag-Erling Smørgrav user_hostfiles[0]); 952af12a3e7SDag-Erling Smørgrav else 953cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added the %s host " 954af12a3e7SDag-Erling Smørgrav "key for IP address '%.128s' to the list " 955af12a3e7SDag-Erling Smørgrav "of known hosts.", type, ip); 956d4af9e69SDag-Erling Smørgrav } else if (options.visual_host_key) { 957bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(host_key, 958bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT); 959bc5531deSDag-Erling Smørgrav ra = sshkey_fingerprint(host_key, 960bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_RANDOMART); 961bc5531deSDag-Erling Smørgrav if (fp == NULL || ra == NULL) 962bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint fail", __func__); 963acc1a9efSDag-Erling Smørgrav logit("Host key fingerprint is %s\n%s", fp, ra); 964e4a9863fSDag-Erling Smørgrav free(ra); 965e4a9863fSDag-Erling Smørgrav free(fp); 966e8aafc91SKris Kennaway } 967bc5531deSDag-Erling Smørgrav hostkey_trusted = 1; 968e8aafc91SKris Kennaway break; 969e8aafc91SKris Kennaway case HOST_NEW: 970333ee039SDag-Erling Smørgrav if (options.host_key_alias == NULL && port != 0 && 971333ee039SDag-Erling Smørgrav port != SSH_DEFAULT_PORT) { 972333ee039SDag-Erling Smørgrav debug("checking without port identifier"); 973cce7d346SDag-Erling Smørgrav if (check_host_key(hostname, hostaddr, 0, host_key, 974e146993eSDag-Erling Smørgrav ROQUIET, user_hostfiles, num_user_hostfiles, 975e146993eSDag-Erling Smørgrav system_hostfiles, num_system_hostfiles) == 0) { 976333ee039SDag-Erling Smørgrav debug("found matching key w/out port"); 977333ee039SDag-Erling Smørgrav break; 978333ee039SDag-Erling Smørgrav } 979333ee039SDag-Erling Smørgrav } 980b15c8340SDag-Erling Smørgrav if (readonly || want_cert) 981af12a3e7SDag-Erling Smørgrav goto fail; 982e8aafc91SKris Kennaway /* The host is new. */ 9834f52dfbbSDag-Erling Smørgrav if (options.strict_host_key_checking == 9844f52dfbbSDag-Erling Smørgrav SSH_STRICT_HOSTKEY_YES) { 985af12a3e7SDag-Erling Smørgrav /* 986af12a3e7SDag-Erling Smørgrav * User has requested strict host key checking. We 987af12a3e7SDag-Erling Smørgrav * will not add the host key automatically. The only 988af12a3e7SDag-Erling Smørgrav * alternative left is to abort. 989af12a3e7SDag-Erling Smørgrav */ 990af12a3e7SDag-Erling Smørgrav error("No %s host key is known for %.200s and you " 991af12a3e7SDag-Erling Smørgrav "have requested strict checking.", type, host); 992af12a3e7SDag-Erling Smørgrav goto fail; 9934f52dfbbSDag-Erling Smørgrav } else if (options.strict_host_key_checking == 9944f52dfbbSDag-Erling Smørgrav SSH_STRICT_HOSTKEY_ASK) { 995cf2b5f3bSDag-Erling Smørgrav char msg1[1024], msg2[1024]; 996cf2b5f3bSDag-Erling Smørgrav 9974a421b63SDag-Erling Smørgrav if (show_other_keys(host_hostkeys, host_key)) 998cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), 999cf2b5f3bSDag-Erling Smørgrav "\nbut keys of different type are already" 1000cf2b5f3bSDag-Erling Smørgrav " known for this host."); 1001cf2b5f3bSDag-Erling Smørgrav else 1002cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), "."); 1003e8aafc91SKris Kennaway /* The default */ 1004bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(host_key, 1005bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT); 1006bc5531deSDag-Erling Smørgrav ra = sshkey_fingerprint(host_key, 1007bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_RANDOMART); 1008bc5531deSDag-Erling Smørgrav if (fp == NULL || ra == NULL) 1009bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint fail", __func__); 1010cf2b5f3bSDag-Erling Smørgrav msg2[0] = '\0'; 1011cf2b5f3bSDag-Erling Smørgrav if (options.verify_host_key_dns) { 10121ec0d754SDag-Erling Smørgrav if (matching_host_key_dns) 1013cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 1014cf2b5f3bSDag-Erling Smørgrav "Matching host key fingerprint" 1015cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 1016cf2b5f3bSDag-Erling Smørgrav else 1017cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 1018cf2b5f3bSDag-Erling Smørgrav "No matching host key fingerprint" 1019cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 1020cf2b5f3bSDag-Erling Smørgrav } 1021af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 1022af12a3e7SDag-Erling Smørgrav "The authenticity of host '%.200s (%s)' can't be " 1023f388f5efSDag-Erling Smørgrav "established%s\n" 1024d4af9e69SDag-Erling Smørgrav "%s key fingerprint is %s.%s%s\n%s" 1025af12a3e7SDag-Erling Smørgrav "Are you sure you want to continue connecting " 1026f388f5efSDag-Erling Smørgrav "(yes/no)? ", 1027d4af9e69SDag-Erling Smørgrav host, ip, msg1, type, fp, 1028d4af9e69SDag-Erling Smørgrav options.visual_host_key ? "\n" : "", 1029d4af9e69SDag-Erling Smørgrav options.visual_host_key ? ra : "", 1030d4af9e69SDag-Erling Smørgrav msg2); 1031e4a9863fSDag-Erling Smørgrav free(ra); 1032e4a9863fSDag-Erling Smørgrav free(fp); 1033af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 1034af12a3e7SDag-Erling Smørgrav goto fail; 1035bc5531deSDag-Erling Smørgrav hostkey_trusted = 1; /* user explicitly confirmed */ 1036e8aafc91SKris Kennaway } 1037af12a3e7SDag-Erling Smørgrav /* 10384f52dfbbSDag-Erling Smørgrav * If in "new" or "off" strict mode, add the key automatically 10394f52dfbbSDag-Erling Smørgrav * to the local known_hosts file. 1040af12a3e7SDag-Erling Smørgrav */ 1041aa49c926SDag-Erling Smørgrav if (options.check_host_ip && ip_status == HOST_NEW) { 10424a421b63SDag-Erling Smørgrav snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 1043aa49c926SDag-Erling Smørgrav hostp = hostline; 1044aa49c926SDag-Erling Smørgrav if (options.hash_known_hosts) { 1045aa49c926SDag-Erling Smørgrav /* Add hash of host and IP separately */ 1046e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], 1047e146993eSDag-Erling Smørgrav host, host_key, options.hash_known_hosts) && 1048e146993eSDag-Erling Smørgrav add_host_to_hostfile(user_hostfiles[0], ip, 1049aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts); 1050aa49c926SDag-Erling Smørgrav } else { 1051aa49c926SDag-Erling Smørgrav /* Add unhashed "host,ip" */ 1052e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], 1053aa49c926SDag-Erling Smørgrav hostline, host_key, 1054aa49c926SDag-Erling Smørgrav options.hash_known_hosts); 1055aa49c926SDag-Erling Smørgrav } 1056aa49c926SDag-Erling Smørgrav } else { 1057e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], host, 1058e146993eSDag-Erling Smørgrav host_key, options.hash_known_hosts); 1059aa49c926SDag-Erling Smørgrav hostp = host; 1060aa49c926SDag-Erling Smørgrav } 1061aa49c926SDag-Erling Smørgrav 1062aa49c926SDag-Erling Smørgrav if (!r) 1063cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the host to the list of known " 1064e146993eSDag-Erling Smørgrav "hosts (%.500s).", user_hostfiles[0]); 1065e8aafc91SKris Kennaway else 1066cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added '%.200s' (%s) to the " 1067af12a3e7SDag-Erling Smørgrav "list of known hosts.", hostp, type); 1068e8aafc91SKris Kennaway break; 1069b15c8340SDag-Erling Smørgrav case HOST_REVOKED: 1070b15c8340SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1071b15c8340SDag-Erling Smørgrav error("@ WARNING: REVOKED HOST KEY DETECTED! @"); 1072b15c8340SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1073b15c8340SDag-Erling Smørgrav error("The %s host key for %s is marked as revoked.", type, host); 1074b15c8340SDag-Erling Smørgrav error("This could mean that a stolen key is being used to"); 1075b15c8340SDag-Erling Smørgrav error("impersonate this host."); 1076b15c8340SDag-Erling Smørgrav 1077b15c8340SDag-Erling Smørgrav /* 1078b15c8340SDag-Erling Smørgrav * If strict host key checking is in use, the user will have 1079b15c8340SDag-Erling Smørgrav * to edit the key manually and we can only abort. 1080b15c8340SDag-Erling Smørgrav */ 10814f52dfbbSDag-Erling Smørgrav if (options.strict_host_key_checking != 10824f52dfbbSDag-Erling Smørgrav SSH_STRICT_HOSTKEY_OFF) { 1083b15c8340SDag-Erling Smørgrav error("%s host key for %.200s was revoked and you have " 1084b15c8340SDag-Erling Smørgrav "requested strict checking.", type, host); 1085b15c8340SDag-Erling Smørgrav goto fail; 1086b15c8340SDag-Erling Smørgrav } 1087b15c8340SDag-Erling Smørgrav goto continue_unsafe; 1088b15c8340SDag-Erling Smørgrav 1089e8aafc91SKris Kennaway case HOST_CHANGED: 1090b15c8340SDag-Erling Smørgrav if (want_cert) { 1091b15c8340SDag-Erling Smørgrav /* 1092b15c8340SDag-Erling Smørgrav * This is only a debug() since it is valid to have 1093b15c8340SDag-Erling Smørgrav * CAs with wildcard DNS matches that don't match 1094b15c8340SDag-Erling Smørgrav * all hosts that one might visit. 1095b15c8340SDag-Erling Smørgrav */ 1096b15c8340SDag-Erling Smørgrav debug("Host certificate authority does not " 10974a421b63SDag-Erling Smørgrav "match %s in %s:%lu", CA_MARKER, 10984a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1099b15c8340SDag-Erling Smørgrav goto fail; 1100b15c8340SDag-Erling Smørgrav } 1101333ee039SDag-Erling Smørgrav if (readonly == ROQUIET) 1102333ee039SDag-Erling Smørgrav goto fail; 1103e8aafc91SKris Kennaway if (options.check_host_ip && host_ip_differ) { 110421e764dfSDag-Erling Smørgrav char *key_msg; 1105e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 110621e764dfSDag-Erling Smørgrav key_msg = "is unknown"; 1107e8aafc91SKris Kennaway else if (ip_status == HOST_OK) 110821e764dfSDag-Erling Smørgrav key_msg = "is unchanged"; 1109e8aafc91SKris Kennaway else 111021e764dfSDag-Erling Smørgrav key_msg = "has a different value"; 1111e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1112e8aafc91SKris Kennaway error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 1113e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1114e8aafc91SKris Kennaway error("The %s host key for %s has changed,", type, host); 1115d4af9e69SDag-Erling Smørgrav error("and the key for the corresponding IP address %s", ip); 111621e764dfSDag-Erling Smørgrav error("%s. This could either mean that", key_msg); 1117e8aafc91SKris Kennaway error("DNS SPOOFING is happening or the IP address for the host"); 1118ca3176e7SBrian Feldman error("and its host key have changed at the same time."); 1119ca3176e7SBrian Feldman if (ip_status != HOST_NEW) 11204a421b63SDag-Erling Smørgrav error("Offending key for IP in %s:%lu", 11214a421b63SDag-Erling Smørgrav ip_found->file, ip_found->line); 1122e8aafc91SKris Kennaway } 1123e8aafc91SKris Kennaway /* The host key has changed. */ 11241ec0d754SDag-Erling Smørgrav warn_changed_key(host_key); 1125e8aafc91SKris Kennaway error("Add correct host key in %.100s to get rid of this message.", 1126e146993eSDag-Erling Smørgrav user_hostfiles[0]); 11274f52dfbbSDag-Erling Smørgrav error("Offending %s key in %s:%lu", 11284f52dfbbSDag-Erling Smørgrav sshkey_type(host_found->key), 11294a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1130e8aafc91SKris Kennaway 1131e8aafc91SKris Kennaway /* 1132e8aafc91SKris Kennaway * If strict host key checking is in use, the user will have 1133e8aafc91SKris Kennaway * to edit the key manually and we can only abort. 1134e8aafc91SKris Kennaway */ 11354f52dfbbSDag-Erling Smørgrav if (options.strict_host_key_checking != 11364f52dfbbSDag-Erling Smørgrav SSH_STRICT_HOSTKEY_OFF) { 1137af12a3e7SDag-Erling Smørgrav error("%s host key for %.200s has changed and you have " 1138af12a3e7SDag-Erling Smørgrav "requested strict checking.", type, host); 1139af12a3e7SDag-Erling Smørgrav goto fail; 1140af12a3e7SDag-Erling Smørgrav } 1141e8aafc91SKris Kennaway 1142b15c8340SDag-Erling Smørgrav continue_unsafe: 1143e8aafc91SKris Kennaway /* 1144e8aafc91SKris Kennaway * If strict host key checking has not been requested, allow 1145cf2b5f3bSDag-Erling Smørgrav * the connection but without MITM-able authentication or 1146333ee039SDag-Erling Smørgrav * forwarding. 1147e8aafc91SKris Kennaway */ 1148e8aafc91SKris Kennaway if (options.password_authentication) { 1149af12a3e7SDag-Erling Smørgrav error("Password authentication is disabled to avoid " 1150af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1151e8aafc91SKris Kennaway options.password_authentication = 0; 1152d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1153e8aafc91SKris Kennaway } 1154cf2b5f3bSDag-Erling Smørgrav if (options.kbd_interactive_authentication) { 1155cf2b5f3bSDag-Erling Smørgrav error("Keyboard-interactive authentication is disabled" 1156cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 1157cf2b5f3bSDag-Erling Smørgrav options.kbd_interactive_authentication = 0; 1158cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 1159d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1160cf2b5f3bSDag-Erling Smørgrav } 1161cf2b5f3bSDag-Erling Smørgrav if (options.challenge_response_authentication) { 1162cf2b5f3bSDag-Erling Smørgrav error("Challenge/response authentication is disabled" 1163cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 1164cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 1165d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1166cf2b5f3bSDag-Erling Smørgrav } 1167e8aafc91SKris Kennaway if (options.forward_agent) { 1168af12a3e7SDag-Erling Smørgrav error("Agent forwarding is disabled to avoid " 1169af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1170e8aafc91SKris Kennaway options.forward_agent = 0; 1171d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1172e8aafc91SKris Kennaway } 1173ca3176e7SBrian Feldman if (options.forward_x11) { 1174af12a3e7SDag-Erling Smørgrav error("X11 forwarding is disabled to avoid " 1175af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1176ca3176e7SBrian Feldman options.forward_x11 = 0; 1177d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1178ca3176e7SBrian Feldman } 1179af12a3e7SDag-Erling Smørgrav if (options.num_local_forwards > 0 || 1180af12a3e7SDag-Erling Smørgrav options.num_remote_forwards > 0) { 1181af12a3e7SDag-Erling Smørgrav error("Port forwarding is disabled to avoid " 1182af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1183af12a3e7SDag-Erling Smørgrav options.num_local_forwards = 1184af12a3e7SDag-Erling Smørgrav options.num_remote_forwards = 0; 1185d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1186ca3176e7SBrian Feldman } 1187333ee039SDag-Erling Smørgrav if (options.tun_open != SSH_TUNMODE_NO) { 1188333ee039SDag-Erling Smørgrav error("Tunnel forwarding is disabled to avoid " 1189333ee039SDag-Erling Smørgrav "man-in-the-middle attacks."); 1190333ee039SDag-Erling Smørgrav options.tun_open = SSH_TUNMODE_NO; 1191d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1192333ee039SDag-Erling Smørgrav } 1193d4af9e69SDag-Erling Smørgrav if (options.exit_on_forward_failure && cancelled_forwarding) 1194d4af9e69SDag-Erling Smørgrav fatal("Error: forwarding disabled due to host key " 1195d4af9e69SDag-Erling Smørgrav "check failure"); 1196d4af9e69SDag-Erling Smørgrav 1197e8aafc91SKris Kennaway /* 1198e8aafc91SKris Kennaway * XXX Should permit the user to change to use the new id. 1199e8aafc91SKris Kennaway * This could be done by converting the host key to an 1200e8aafc91SKris Kennaway * identifying sentence, tell that the host identifies itself 1201b15c8340SDag-Erling Smørgrav * by that sentence, and ask the user if he/she wishes to 1202e8aafc91SKris Kennaway * accept the authentication. 1203e8aafc91SKris Kennaway */ 1204e8aafc91SKris Kennaway break; 1205f388f5efSDag-Erling Smørgrav case HOST_FOUND: 1206f388f5efSDag-Erling Smørgrav fatal("internal error"); 1207f388f5efSDag-Erling Smørgrav break; 1208e8aafc91SKris Kennaway } 1209ca3176e7SBrian Feldman 1210ca3176e7SBrian Feldman if (options.check_host_ip && host_status != HOST_CHANGED && 1211ca3176e7SBrian Feldman ip_status == HOST_CHANGED) { 1212af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 1213af12a3e7SDag-Erling Smørgrav "Warning: the %s host key for '%.200s' " 1214af12a3e7SDag-Erling Smørgrav "differs from the key for the IP address '%.128s'" 12154a421b63SDag-Erling Smørgrav "\nOffending key for IP in %s:%lu", 12164a421b63SDag-Erling Smørgrav type, host, ip, ip_found->file, ip_found->line); 1217af12a3e7SDag-Erling Smørgrav if (host_status == HOST_OK) { 1218af12a3e7SDag-Erling Smørgrav len = strlen(msg); 1219af12a3e7SDag-Erling Smørgrav snprintf(msg + len, sizeof(msg) - len, 12204a421b63SDag-Erling Smørgrav "\nMatching host key in %s:%lu", 12214a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1222af12a3e7SDag-Erling Smørgrav } 12234f52dfbbSDag-Erling Smørgrav if (options.strict_host_key_checking == 12244f52dfbbSDag-Erling Smørgrav SSH_STRICT_HOSTKEY_ASK) { 1225af12a3e7SDag-Erling Smørgrav strlcat(msg, "\nAre you sure you want " 1226af12a3e7SDag-Erling Smørgrav "to continue connecting (yes/no)? ", sizeof(msg)); 1227af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 1228af12a3e7SDag-Erling Smørgrav goto fail; 12294f52dfbbSDag-Erling Smørgrav } else if (options.strict_host_key_checking != 12304f52dfbbSDag-Erling Smørgrav SSH_STRICT_HOSTKEY_OFF) { 12314f52dfbbSDag-Erling Smørgrav logit("%s", msg); 12324f52dfbbSDag-Erling Smørgrav error("Exiting, you have requested strict checking."); 12334f52dfbbSDag-Erling Smørgrav goto fail; 1234af12a3e7SDag-Erling Smørgrav } else { 1235cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 1236ca3176e7SBrian Feldman } 1237ca3176e7SBrian Feldman } 1238ca3176e7SBrian Feldman 1239bc5531deSDag-Erling Smørgrav if (!hostkey_trusted && options.update_hostkeys) { 1240bc5531deSDag-Erling Smørgrav debug("%s: hostkey not known or explicitly trusted: " 1241bc5531deSDag-Erling Smørgrav "disabling UpdateHostkeys", __func__); 1242bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 1243bc5531deSDag-Erling Smørgrav } 1244bc5531deSDag-Erling Smørgrav 1245e4a9863fSDag-Erling Smørgrav free(ip); 1246e4a9863fSDag-Erling Smørgrav free(host); 12474a421b63SDag-Erling Smørgrav if (host_hostkeys != NULL) 12484a421b63SDag-Erling Smørgrav free_hostkeys(host_hostkeys); 12494a421b63SDag-Erling Smørgrav if (ip_hostkeys != NULL) 12504a421b63SDag-Erling Smørgrav free_hostkeys(ip_hostkeys); 1251af12a3e7SDag-Erling Smørgrav return 0; 1252af12a3e7SDag-Erling Smørgrav 1253af12a3e7SDag-Erling Smørgrav fail: 1254b15c8340SDag-Erling Smørgrav if (want_cert && host_status != HOST_REVOKED) { 1255b15c8340SDag-Erling Smørgrav /* 1256b15c8340SDag-Erling Smørgrav * No matching certificate. Downgrade cert to raw key and 1257b15c8340SDag-Erling Smørgrav * search normally. 1258b15c8340SDag-Erling Smørgrav */ 1259b15c8340SDag-Erling Smørgrav debug("No matching CA found. Retry with plain key"); 12604f52dfbbSDag-Erling Smørgrav if ((r = sshkey_from_private(host_key, &raw_key)) != 0) 12614f52dfbbSDag-Erling Smørgrav fatal("%s: sshkey_from_private: %s", 12624f52dfbbSDag-Erling Smørgrav __func__, ssh_err(r)); 12634f52dfbbSDag-Erling Smørgrav if ((r = sshkey_drop_cert(raw_key)) != 0) 12644f52dfbbSDag-Erling Smørgrav fatal("Couldn't drop certificate: %s", ssh_err(r)); 1265b15c8340SDag-Erling Smørgrav host_key = raw_key; 1266b15c8340SDag-Erling Smørgrav goto retry; 1267b15c8340SDag-Erling Smørgrav } 12684f52dfbbSDag-Erling Smørgrav sshkey_free(raw_key); 1269e4a9863fSDag-Erling Smørgrav free(ip); 1270e4a9863fSDag-Erling Smørgrav free(host); 12714a421b63SDag-Erling Smørgrav if (host_hostkeys != NULL) 12724a421b63SDag-Erling Smørgrav free_hostkeys(host_hostkeys); 12734a421b63SDag-Erling Smørgrav if (ip_hostkeys != NULL) 12744a421b63SDag-Erling Smørgrav free_hostkeys(ip_hostkeys); 1275af12a3e7SDag-Erling Smørgrav return -1; 1276e8aafc91SKris Kennaway } 1277511b41d2SMark Murray 1278cf2b5f3bSDag-Erling Smørgrav /* returns 0 if key verifies or -1 if key does NOT verify */ 1279fe5fd017SMark Murray int 12804f52dfbbSDag-Erling Smørgrav verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key) 1281fe5fd017SMark Murray { 1282acc1a9efSDag-Erling Smørgrav u_int i; 1283a0ee8cc6SDag-Erling Smørgrav int r = -1, flags = 0; 1284acc1a9efSDag-Erling Smørgrav char valid[64], *fp = NULL, *cafp = NULL; 1285bc5531deSDag-Erling Smørgrav struct sshkey *plain = NULL; 12864a421b63SDag-Erling Smørgrav 1287bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(host_key, 1288bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1289bc5531deSDag-Erling Smørgrav error("%s: fingerprint host key: %s", __func__, ssh_err(r)); 1290bc5531deSDag-Erling Smørgrav r = -1; 1291bc5531deSDag-Erling Smørgrav goto out; 1292bc5531deSDag-Erling Smørgrav } 1293fe5fd017SMark Murray 1294acc1a9efSDag-Erling Smørgrav if (sshkey_is_cert(host_key)) { 1295acc1a9efSDag-Erling Smørgrav if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, 1296acc1a9efSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1297acc1a9efSDag-Erling Smørgrav error("%s: fingerprint CA key: %s", 1298acc1a9efSDag-Erling Smørgrav __func__, ssh_err(r)); 1299acc1a9efSDag-Erling Smørgrav r = -1; 1300acc1a9efSDag-Erling Smørgrav goto out; 1301acc1a9efSDag-Erling Smørgrav } 1302acc1a9efSDag-Erling Smørgrav sshkey_format_cert_validity(host_key->cert, 1303acc1a9efSDag-Erling Smørgrav valid, sizeof(valid)); 1304acc1a9efSDag-Erling Smørgrav debug("Server host certificate: %s %s, serial %llu " 1305acc1a9efSDag-Erling Smørgrav "ID \"%s\" CA %s %s valid %s", 1306acc1a9efSDag-Erling Smørgrav sshkey_ssh_name(host_key), fp, 1307acc1a9efSDag-Erling Smørgrav (unsigned long long)host_key->cert->serial, 1308acc1a9efSDag-Erling Smørgrav host_key->cert->key_id, 1309acc1a9efSDag-Erling Smørgrav sshkey_ssh_name(host_key->cert->signature_key), cafp, 1310acc1a9efSDag-Erling Smørgrav valid); 1311acc1a9efSDag-Erling Smørgrav for (i = 0; i < host_key->cert->nprincipals; i++) { 1312acc1a9efSDag-Erling Smørgrav debug2("Server host certificate hostname: %s", 1313acc1a9efSDag-Erling Smørgrav host_key->cert->principals[i]); 1314acc1a9efSDag-Erling Smørgrav } 1315acc1a9efSDag-Erling Smørgrav } else { 13164f52dfbbSDag-Erling Smørgrav debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp); 1317acc1a9efSDag-Erling Smørgrav } 1318bc5531deSDag-Erling Smørgrav 1319bc5531deSDag-Erling Smørgrav if (sshkey_equal(previous_host_key, host_key)) { 1320bc5531deSDag-Erling Smørgrav debug2("%s: server host key %s %s matches cached key", 1321bc5531deSDag-Erling Smørgrav __func__, sshkey_type(host_key), fp); 1322bc5531deSDag-Erling Smørgrav r = 0; 1323bc5531deSDag-Erling Smørgrav goto out; 1324bc5531deSDag-Erling Smørgrav } 1325bc5531deSDag-Erling Smørgrav 1326bc5531deSDag-Erling Smørgrav /* Check in RevokedHostKeys file if specified */ 1327bc5531deSDag-Erling Smørgrav if (options.revoked_host_keys != NULL) { 1328bc5531deSDag-Erling Smørgrav r = sshkey_check_revoked(host_key, options.revoked_host_keys); 1329bc5531deSDag-Erling Smørgrav switch (r) { 1330bc5531deSDag-Erling Smørgrav case 0: 1331bc5531deSDag-Erling Smørgrav break; /* not revoked */ 1332bc5531deSDag-Erling Smørgrav case SSH_ERR_KEY_REVOKED: 1333bc5531deSDag-Erling Smørgrav error("Host key %s %s revoked by file %s", 1334bc5531deSDag-Erling Smørgrav sshkey_type(host_key), fp, 1335bc5531deSDag-Erling Smørgrav options.revoked_host_keys); 1336bc5531deSDag-Erling Smørgrav r = -1; 1337bc5531deSDag-Erling Smørgrav goto out; 1338bc5531deSDag-Erling Smørgrav default: 1339bc5531deSDag-Erling Smørgrav error("Error checking host key %s %s in " 1340bc5531deSDag-Erling Smørgrav "revoked keys file %s: %s", sshkey_type(host_key), 1341bc5531deSDag-Erling Smørgrav fp, options.revoked_host_keys, ssh_err(r)); 1342bc5531deSDag-Erling Smørgrav r = -1; 1343bc5531deSDag-Erling Smørgrav goto out; 1344bc5531deSDag-Erling Smørgrav } 1345a0ee8cc6SDag-Erling Smørgrav } 1346a0ee8cc6SDag-Erling Smørgrav 13473a0b9b77SXin LI if (options.verify_host_key_dns) { 13483a0b9b77SXin LI /* 13493a0b9b77SXin LI * XXX certs are not yet supported for DNS, so downgrade 13503a0b9b77SXin LI * them and try the plain key. 13513a0b9b77SXin LI */ 1352bc5531deSDag-Erling Smørgrav if ((r = sshkey_from_private(host_key, &plain)) != 0) 1353bc5531deSDag-Erling Smørgrav goto out; 1354bc5531deSDag-Erling Smørgrav if (sshkey_is_cert(plain)) 1355bc5531deSDag-Erling Smørgrav sshkey_drop_cert(plain); 13563a0b9b77SXin LI if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) { 13571ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_FOUND) { 13581ec0d754SDag-Erling Smørgrav if (options.verify_host_key_dns == 1 && 13591ec0d754SDag-Erling Smørgrav flags & DNS_VERIFY_MATCH && 13603a0b9b77SXin LI flags & DNS_VERIFY_SECURE) { 1361a0ee8cc6SDag-Erling Smørgrav r = 0; 1362bc5531deSDag-Erling Smørgrav goto out; 13633a0b9b77SXin LI } 13641ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_MATCH) { 13651ec0d754SDag-Erling Smørgrav matching_host_key_dns = 1; 13661ec0d754SDag-Erling Smørgrav } else { 13673a0b9b77SXin LI warn_changed_key(plain); 13683a0b9b77SXin LI error("Update the SSHFP RR in DNS " 13693a0b9b77SXin LI "with the new host key to get rid " 13703a0b9b77SXin LI "of this message."); 1371cf2b5f3bSDag-Erling Smørgrav } 1372cf2b5f3bSDag-Erling Smørgrav } 13731ec0d754SDag-Erling Smørgrav } 13743a0b9b77SXin LI } 1375a0ee8cc6SDag-Erling Smørgrav r = check_host_key(host, hostaddr, options.port, host_key, RDRW, 1376e146993eSDag-Erling Smørgrav options.user_hostfiles, options.num_user_hostfiles, 1377e146993eSDag-Erling Smørgrav options.system_hostfiles, options.num_system_hostfiles); 1378a0ee8cc6SDag-Erling Smørgrav 1379bc5531deSDag-Erling Smørgrav out: 1380bc5531deSDag-Erling Smørgrav sshkey_free(plain); 1381bc5531deSDag-Erling Smørgrav free(fp); 1382acc1a9efSDag-Erling Smørgrav free(cafp); 1383a0ee8cc6SDag-Erling Smørgrav if (r == 0 && host_key != NULL) { 13844f52dfbbSDag-Erling Smørgrav sshkey_free(previous_host_key); 13854f52dfbbSDag-Erling Smørgrav r = sshkey_from_private(host_key, &previous_host_key); 1386a0ee8cc6SDag-Erling Smørgrav } 1387a0ee8cc6SDag-Erling Smørgrav 1388a0ee8cc6SDag-Erling Smørgrav return r; 1389fe5fd017SMark Murray } 1390fe5fd017SMark Murray 1391511b41d2SMark Murray /* 1392511b41d2SMark Murray * Starts a dialog with the server, and authenticates the current user on the 1393511b41d2SMark Murray * server. This does not need any extra privileges. The basic connection 1394511b41d2SMark Murray * to the server must already have been established before this is called. 1395511b41d2SMark Murray * If login fails, this function prints an error and never returns. 1396511b41d2SMark Murray * This function does not require super-user privileges. 1397511b41d2SMark Murray */ 1398511b41d2SMark Murray void 139980628bacSDag-Erling Smørgrav ssh_login(Sensitive *sensitive, const char *orighost, 14004a421b63SDag-Erling Smørgrav struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) 1401511b41d2SMark Murray { 1402f7167e0eSDag-Erling Smørgrav char *host; 1403e8aafc91SKris Kennaway char *server_user, *local_user; 1404e8aafc91SKris Kennaway 1405e8aafc91SKris Kennaway local_user = xstrdup(pw->pw_name); 1406e8aafc91SKris Kennaway server_user = options.user ? options.user : local_user; 1407511b41d2SMark Murray 1408511b41d2SMark Murray /* Convert the user-supplied hostname into all lowercase. */ 1409511b41d2SMark Murray host = xstrdup(orighost); 1410f7167e0eSDag-Erling Smørgrav lowercase(host); 1411511b41d2SMark Murray 1412511b41d2SMark Murray /* Exchange protocol version identification strings with the server. */ 1413d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(timeout_ms); 1414511b41d2SMark Murray 1415511b41d2SMark Murray /* Put the connection into non-blocking mode. */ 1416511b41d2SMark Murray packet_set_nonblocking(); 1417511b41d2SMark Murray 1418511b41d2SMark Murray /* key exchange */ 1419511b41d2SMark Murray /* authenticate user */ 1420557f75e5SDag-Erling Smørgrav debug("Authenticating to %s:%d as '%s'", host, port, server_user); 14214a421b63SDag-Erling Smørgrav ssh_kex2(host, hostaddr, port); 142280628bacSDag-Erling Smørgrav ssh_userauth2(local_user, server_user, host, sensitive); 1423e4a9863fSDag-Erling Smørgrav free(local_user); 1424511b41d2SMark Murray } 1425e0fbb1d2SBrian Feldman 1426e0fbb1d2SBrian Feldman void 1427e0fbb1d2SBrian Feldman ssh_put_password(char *password) 1428e0fbb1d2SBrian Feldman { 1429e0fbb1d2SBrian Feldman int size; 1430e0fbb1d2SBrian Feldman char *padded; 1431e0fbb1d2SBrian Feldman 1432ca3176e7SBrian Feldman if (datafellows & SSH_BUG_PASSWORDPAD) { 1433af12a3e7SDag-Erling Smørgrav packet_put_cstring(password); 1434ca3176e7SBrian Feldman return; 1435ca3176e7SBrian Feldman } 1436ca86bcf2SDag-Erling Smørgrav size = ROUNDUP(strlen(password) + 1, 32); 1437333ee039SDag-Erling Smørgrav padded = xcalloc(1, size); 1438e0fbb1d2SBrian Feldman strlcpy(padded, password, size); 1439e0fbb1d2SBrian Feldman packet_put_string(padded, size); 1440b83788ffSDag-Erling Smørgrav explicit_bzero(padded, size); 1441e4a9863fSDag-Erling Smørgrav free(padded); 1442e0fbb1d2SBrian Feldman } 1443f388f5efSDag-Erling Smørgrav 1444f388f5efSDag-Erling Smørgrav /* print all known host keys for a given host, but skip keys of given type */ 1445f388f5efSDag-Erling Smørgrav static int 14464f52dfbbSDag-Erling Smørgrav show_other_keys(struct hostkeys *hostkeys, struct sshkey *key) 1447f388f5efSDag-Erling Smørgrav { 1448f7167e0eSDag-Erling Smørgrav int type[] = { 1449f7167e0eSDag-Erling Smørgrav KEY_RSA, 1450f7167e0eSDag-Erling Smørgrav KEY_DSA, 1451f7167e0eSDag-Erling Smørgrav KEY_ECDSA, 1452f7167e0eSDag-Erling Smørgrav KEY_ED25519, 145347dd1d1bSDag-Erling Smørgrav KEY_XMSS, 1454f7167e0eSDag-Erling Smørgrav -1 1455f7167e0eSDag-Erling Smørgrav }; 14564a421b63SDag-Erling Smørgrav int i, ret = 0; 14574a421b63SDag-Erling Smørgrav char *fp, *ra; 14584a421b63SDag-Erling Smørgrav const struct hostkey_entry *found; 1459f388f5efSDag-Erling Smørgrav 1460f388f5efSDag-Erling Smørgrav for (i = 0; type[i] != -1; i++) { 1461f388f5efSDag-Erling Smørgrav if (type[i] == key->type) 1462f388f5efSDag-Erling Smørgrav continue; 14634a421b63SDag-Erling Smørgrav if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) 1464f388f5efSDag-Erling Smørgrav continue; 1465bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(found->key, 1466bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT); 1467bc5531deSDag-Erling Smørgrav ra = sshkey_fingerprint(found->key, 1468bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_RANDOMART); 1469bc5531deSDag-Erling Smørgrav if (fp == NULL || ra == NULL) 1470bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint fail", __func__); 14714a421b63SDag-Erling Smørgrav logit("WARNING: %s key found for host %s\n" 14724a421b63SDag-Erling Smørgrav "in %s:%lu\n" 14734a421b63SDag-Erling Smørgrav "%s key fingerprint %s.", 1474*190cef3dSDag-Erling Smørgrav sshkey_type(found->key), 14754a421b63SDag-Erling Smørgrav found->host, found->file, found->line, 1476*190cef3dSDag-Erling Smørgrav sshkey_type(found->key), fp); 14774a421b63SDag-Erling Smørgrav if (options.visual_host_key) 14784a421b63SDag-Erling Smørgrav logit("%s", ra); 1479e4a9863fSDag-Erling Smørgrav free(ra); 1480e4a9863fSDag-Erling Smørgrav free(fp); 14814a421b63SDag-Erling Smørgrav ret = 1; 1482f388f5efSDag-Erling Smørgrav } 14834a421b63SDag-Erling Smørgrav return ret; 1484f388f5efSDag-Erling Smørgrav } 14851ec0d754SDag-Erling Smørgrav 14861ec0d754SDag-Erling Smørgrav static void 14874f52dfbbSDag-Erling Smørgrav warn_changed_key(struct sshkey *host_key) 14881ec0d754SDag-Erling Smørgrav { 14891ec0d754SDag-Erling Smørgrav char *fp; 14901ec0d754SDag-Erling Smørgrav 1491bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(host_key, options.fingerprint_hash, 1492bc5531deSDag-Erling Smørgrav SSH_FP_DEFAULT); 1493bc5531deSDag-Erling Smørgrav if (fp == NULL) 1494bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint fail", __func__); 14951ec0d754SDag-Erling Smørgrav 14961ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 14971ec0d754SDag-Erling Smørgrav error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 14981ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 14991ec0d754SDag-Erling Smørgrav error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 15001ec0d754SDag-Erling Smørgrav error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 15014a421b63SDag-Erling Smørgrav error("It is also possible that a host key has just been changed."); 15021ec0d754SDag-Erling Smørgrav error("The fingerprint for the %s key sent by the remote host is\n%s.", 1503*190cef3dSDag-Erling Smørgrav sshkey_type(host_key), fp); 15041ec0d754SDag-Erling Smørgrav error("Please contact your system administrator."); 15051ec0d754SDag-Erling Smørgrav 1506e4a9863fSDag-Erling Smørgrav free(fp); 15071ec0d754SDag-Erling Smørgrav } 1508b74df5b2SDag-Erling Smørgrav 1509b74df5b2SDag-Erling Smørgrav /* 1510b74df5b2SDag-Erling Smørgrav * Execute a local command 1511b74df5b2SDag-Erling Smørgrav */ 1512b74df5b2SDag-Erling Smørgrav int 1513b74df5b2SDag-Erling Smørgrav ssh_local_cmd(const char *args) 1514b74df5b2SDag-Erling Smørgrav { 1515b74df5b2SDag-Erling Smørgrav char *shell; 1516b74df5b2SDag-Erling Smørgrav pid_t pid; 1517b74df5b2SDag-Erling Smørgrav int status; 15184a421b63SDag-Erling Smørgrav void (*osighand)(int); 1519b74df5b2SDag-Erling Smørgrav 1520b74df5b2SDag-Erling Smørgrav if (!options.permit_local_command || 1521b74df5b2SDag-Erling Smørgrav args == NULL || !*args) 1522b74df5b2SDag-Erling Smørgrav return (1); 1523b74df5b2SDag-Erling Smørgrav 15244a421b63SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 1525b74df5b2SDag-Erling Smørgrav shell = _PATH_BSHELL; 1526b74df5b2SDag-Erling Smørgrav 15274a421b63SDag-Erling Smørgrav osighand = signal(SIGCHLD, SIG_DFL); 1528b74df5b2SDag-Erling Smørgrav pid = fork(); 1529b74df5b2SDag-Erling Smørgrav if (pid == 0) { 15304a421b63SDag-Erling Smørgrav signal(SIGPIPE, SIG_DFL); 1531b74df5b2SDag-Erling Smørgrav debug3("Executing %s -c \"%s\"", shell, args); 1532b74df5b2SDag-Erling Smørgrav execl(shell, shell, "-c", args, (char *)NULL); 1533b74df5b2SDag-Erling Smørgrav error("Couldn't execute %s -c \"%s\": %s", 1534b74df5b2SDag-Erling Smørgrav shell, args, strerror(errno)); 1535b74df5b2SDag-Erling Smørgrav _exit(1); 1536b74df5b2SDag-Erling Smørgrav } else if (pid == -1) 1537b74df5b2SDag-Erling Smørgrav fatal("fork failed: %.100s", strerror(errno)); 1538b74df5b2SDag-Erling Smørgrav while (waitpid(pid, &status, 0) == -1) 1539b74df5b2SDag-Erling Smørgrav if (errno != EINTR) 1540b74df5b2SDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 15414a421b63SDag-Erling Smørgrav signal(SIGCHLD, osighand); 1542b74df5b2SDag-Erling Smørgrav 1543b74df5b2SDag-Erling Smørgrav if (!WIFEXITED(status)) 1544b74df5b2SDag-Erling Smørgrav return (1); 1545b74df5b2SDag-Erling Smørgrav 1546b74df5b2SDag-Erling Smørgrav return (WEXITSTATUS(status)); 1547b74df5b2SDag-Erling Smørgrav } 1548acc1a9efSDag-Erling Smørgrav 1549acc1a9efSDag-Erling Smørgrav void 155047dd1d1bSDag-Erling Smørgrav maybe_add_key_to_agent(char *authfile, const struct sshkey *private, 155147dd1d1bSDag-Erling Smørgrav char *comment, char *passphrase) 1552acc1a9efSDag-Erling Smørgrav { 1553acc1a9efSDag-Erling Smørgrav int auth_sock = -1, r; 1554acc1a9efSDag-Erling Smørgrav 1555acc1a9efSDag-Erling Smørgrav if (options.add_keys_to_agent == 0) 1556acc1a9efSDag-Erling Smørgrav return; 1557acc1a9efSDag-Erling Smørgrav 1558acc1a9efSDag-Erling Smørgrav if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { 1559acc1a9efSDag-Erling Smørgrav debug3("no authentication agent, not adding key"); 1560acc1a9efSDag-Erling Smørgrav return; 1561acc1a9efSDag-Erling Smørgrav } 1562acc1a9efSDag-Erling Smørgrav 1563acc1a9efSDag-Erling Smørgrav if (options.add_keys_to_agent == 2 && 1564acc1a9efSDag-Erling Smørgrav !ask_permission("Add key %s (%s) to agent?", authfile, comment)) { 1565acc1a9efSDag-Erling Smørgrav debug3("user denied adding this key"); 1566d93a896eSDag-Erling Smørgrav close(auth_sock); 1567acc1a9efSDag-Erling Smørgrav return; 1568acc1a9efSDag-Erling Smørgrav } 1569acc1a9efSDag-Erling Smørgrav 1570acc1a9efSDag-Erling Smørgrav if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, 157147dd1d1bSDag-Erling Smørgrav (options.add_keys_to_agent == 3), 0)) == 0) 1572acc1a9efSDag-Erling Smørgrav debug("identity added to agent: %s", authfile); 1573acc1a9efSDag-Erling Smørgrav else 1574acc1a9efSDag-Erling Smørgrav debug("could not add identity to agent: %s (%d)", authfile, r); 1575d93a896eSDag-Erling Smørgrav close(auth_sock); 1576acc1a9efSDag-Erling Smørgrav } 1577