1*4f52dfbbSDag-Erling Smørgrav /* $OpenBSD: sshconnect.c,v 1.287 2017/09/14 04:32:21 djm 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 27333ee039SDag-Erling Smørgrav #include <netinet/in.h> 28333ee039SDag-Erling Smørgrav #include <arpa/inet.h> 29f7167e0eSDag-Erling Smørgrav #include <rpc/rpc.h> 30333ee039SDag-Erling Smørgrav 31333ee039SDag-Erling Smørgrav #include <ctype.h> 32333ee039SDag-Erling Smørgrav #include <errno.h> 33b15c8340SDag-Erling Smørgrav #include <fcntl.h> 34333ee039SDag-Erling Smørgrav #include <netdb.h> 35333ee039SDag-Erling Smørgrav #ifdef HAVE_PATHS_H 36333ee039SDag-Erling Smørgrav #include <paths.h> 37333ee039SDag-Erling Smørgrav #endif 38333ee039SDag-Erling Smørgrav #include <pwd.h> 39*4f52dfbbSDag-Erling Smørgrav #ifdef HAVE_POLL_H 40*4f52dfbbSDag-Erling Smørgrav #include <poll.h> 41*4f52dfbbSDag-Erling Smørgrav #endif 424a421b63SDag-Erling Smørgrav #include <signal.h> 43333ee039SDag-Erling Smørgrav #include <stdarg.h> 44333ee039SDag-Erling Smørgrav #include <stdio.h> 45333ee039SDag-Erling Smørgrav #include <stdlib.h> 46333ee039SDag-Erling Smørgrav #include <string.h> 47333ee039SDag-Erling Smørgrav #include <unistd.h> 48333ee039SDag-Erling Smørgrav 49511b41d2SMark Murray #include "xmalloc.h" 50333ee039SDag-Erling Smørgrav #include "key.h" 51333ee039SDag-Erling Smørgrav #include "hostfile.h" 52333ee039SDag-Erling Smørgrav #include "ssh.h" 53e8aafc91SKris Kennaway #include "buffer.h" 54511b41d2SMark Murray #include "packet.h" 55511b41d2SMark Murray #include "uidswap.h" 56511b41d2SMark Murray #include "compat.h" 573c6ae118SKris Kennaway #include "key.h" 58e8aafc91SKris Kennaway #include "sshconnect.h" 593c6ae118SKris Kennaway #include "hostfile.h" 60ca3176e7SBrian Feldman #include "log.h" 61a0ee8cc6SDag-Erling Smørgrav #include "misc.h" 62ca3176e7SBrian Feldman #include "readconf.h" 63ca3176e7SBrian Feldman #include "atomicio.h" 64cf2b5f3bSDag-Erling Smørgrav #include "dns.h" 65f7167e0eSDag-Erling Smørgrav #include "monitor_fdpass.h" 66b15c8340SDag-Erling Smørgrav #include "ssh2.h" 67333ee039SDag-Erling Smørgrav #include "version.h" 68bc5531deSDag-Erling Smørgrav #include "authfile.h" 69bc5531deSDag-Erling Smørgrav #include "ssherr.h" 70acc1a9efSDag-Erling Smørgrav #include "authfd.h" 71cf2b5f3bSDag-Erling Smørgrav 72e8aafc91SKris Kennaway char *client_version_string = NULL; 73e8aafc91SKris Kennaway char *server_version_string = NULL; 74*4f52dfbbSDag-Erling Smørgrav struct sshkey *previous_host_key = NULL; 75511b41d2SMark Murray 76b74df5b2SDag-Erling Smørgrav static int matching_host_key_dns = 0; 77cf2b5f3bSDag-Erling Smørgrav 784a421b63SDag-Erling Smørgrav static pid_t proxy_command_pid = 0; 794a421b63SDag-Erling Smørgrav 8080628bacSDag-Erling Smørgrav /* import */ 81511b41d2SMark Murray extern Options options; 82511b41d2SMark Murray extern char *__progname; 8380628bacSDag-Erling Smørgrav extern uid_t original_real_uid; 8480628bacSDag-Erling Smørgrav extern uid_t original_effective_uid; 85511b41d2SMark Murray 86*4f52dfbbSDag-Erling Smørgrav static int show_other_keys(struct hostkeys *, struct sshkey *); 87*4f52dfbbSDag-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 109*4f52dfbbSDag-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 /* Child. Permanently give up superuser privileges. */ 133f7167e0eSDag-Erling Smørgrav permanently_drop_suid(original_real_uid); 134f7167e0eSDag-Erling Smørgrav 135f7167e0eSDag-Erling Smørgrav close(sp[1]); 136f7167e0eSDag-Erling Smørgrav /* Redirect stdin and stdout. */ 137f7167e0eSDag-Erling Smørgrav if (sp[0] != 0) { 138f7167e0eSDag-Erling Smørgrav if (dup2(sp[0], 0) < 0) 139f7167e0eSDag-Erling Smørgrav perror("dup2 stdin"); 140f7167e0eSDag-Erling Smørgrav } 141f7167e0eSDag-Erling Smørgrav if (sp[0] != 1) { 142f7167e0eSDag-Erling Smørgrav if (dup2(sp[0], 1) < 0) 143f7167e0eSDag-Erling Smørgrav perror("dup2 stdout"); 144f7167e0eSDag-Erling Smørgrav } 145f7167e0eSDag-Erling Smørgrav if (sp[0] >= 2) 146f7167e0eSDag-Erling Smørgrav close(sp[0]); 147f7167e0eSDag-Erling Smørgrav 148f7167e0eSDag-Erling Smørgrav /* 149f7167e0eSDag-Erling Smørgrav * Stderr is left as it is so that error messages get 150f7167e0eSDag-Erling Smørgrav * printed on the user's terminal. 151f7167e0eSDag-Erling Smørgrav */ 152f7167e0eSDag-Erling Smørgrav argv[0] = shell; 153f7167e0eSDag-Erling Smørgrav argv[1] = "-c"; 154f7167e0eSDag-Erling Smørgrav argv[2] = command_string; 155f7167e0eSDag-Erling Smørgrav argv[3] = NULL; 156f7167e0eSDag-Erling Smørgrav 157f7167e0eSDag-Erling Smørgrav /* 158f7167e0eSDag-Erling Smørgrav * Execute the proxy command. 159f7167e0eSDag-Erling Smørgrav * Note that we gave up any extra privileges above. 160f7167e0eSDag-Erling Smørgrav */ 161f7167e0eSDag-Erling Smørgrav execv(argv[0], argv); 162f7167e0eSDag-Erling Smørgrav perror(argv[0]); 163f7167e0eSDag-Erling Smørgrav exit(1); 164f7167e0eSDag-Erling Smørgrav } 165f7167e0eSDag-Erling Smørgrav /* Parent. */ 166f7167e0eSDag-Erling Smørgrav if (pid < 0) 167f7167e0eSDag-Erling Smørgrav fatal("fork failed: %.100s", strerror(errno)); 168f7167e0eSDag-Erling Smørgrav close(sp[0]); 169f7167e0eSDag-Erling Smørgrav free(command_string); 170f7167e0eSDag-Erling Smørgrav 171f7167e0eSDag-Erling Smørgrav if ((sock = mm_receive_fd(sp[1])) == -1) 172f7167e0eSDag-Erling Smørgrav fatal("proxy dialer did not pass back a connection"); 173acc1a9efSDag-Erling Smørgrav close(sp[1]); 174f7167e0eSDag-Erling Smørgrav 175f7167e0eSDag-Erling Smørgrav while (waitpid(pid, NULL, 0) == -1) 176f7167e0eSDag-Erling Smørgrav if (errno != EINTR) 177f7167e0eSDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 178f7167e0eSDag-Erling Smørgrav 179f7167e0eSDag-Erling Smørgrav /* Set the connection file descriptors. */ 180*4f52dfbbSDag-Erling Smørgrav if (ssh_packet_set_connection(ssh, sock, sock) == NULL) 181*4f52dfbbSDag-Erling Smørgrav return -1; /* ssh_packet_set_connection logs error */ 182f7167e0eSDag-Erling Smørgrav 183f7167e0eSDag-Erling Smørgrav return 0; 184f7167e0eSDag-Erling Smørgrav } 185f7167e0eSDag-Erling Smørgrav 186511b41d2SMark Murray /* 187511b41d2SMark Murray * Connect to the given ssh server using a proxy command. 188511b41d2SMark Murray */ 189af12a3e7SDag-Erling Smørgrav static int 190*4f52dfbbSDag-Erling Smørgrav ssh_proxy_connect(struct ssh *ssh, const char *host, u_short port, 191*4f52dfbbSDag-Erling Smørgrav const char *proxy_command) 192511b41d2SMark Murray { 193f7167e0eSDag-Erling Smørgrav char *command_string; 194511b41d2SMark Murray int pin[2], pout[2]; 195e8aafc91SKris Kennaway pid_t pid; 196f7167e0eSDag-Erling Smørgrav char *shell; 197420bce64SDag-Erling Smørgrav 1984a421b63SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 199d4af9e69SDag-Erling Smørgrav shell = _PATH_BSHELL; 200511b41d2SMark Murray 201511b41d2SMark Murray /* Create pipes for communicating with the proxy. */ 202511b41d2SMark Murray if (pipe(pin) < 0 || pipe(pout) < 0) 203511b41d2SMark Murray fatal("Could not create pipes to communicate with the proxy: %.100s", 204511b41d2SMark Murray strerror(errno)); 205511b41d2SMark Murray 206f7167e0eSDag-Erling Smørgrav command_string = expand_proxy_command(proxy_command, options.user, 207f7167e0eSDag-Erling Smørgrav host, port); 208511b41d2SMark Murray debug("Executing proxy command: %.500s", command_string); 209511b41d2SMark Murray 210511b41d2SMark Murray /* Fork and execute the proxy command. */ 211511b41d2SMark Murray if ((pid = fork()) == 0) { 212511b41d2SMark Murray char *argv[10]; 213511b41d2SMark Murray 214511b41d2SMark Murray /* Child. Permanently give up superuser privileges. */ 215333ee039SDag-Erling Smørgrav permanently_drop_suid(original_real_uid); 216511b41d2SMark Murray 217511b41d2SMark Murray /* Redirect stdin and stdout. */ 218511b41d2SMark Murray close(pin[1]); 219511b41d2SMark Murray if (pin[0] != 0) { 220511b41d2SMark Murray if (dup2(pin[0], 0) < 0) 221511b41d2SMark Murray perror("dup2 stdin"); 222511b41d2SMark Murray close(pin[0]); 223511b41d2SMark Murray } 224511b41d2SMark Murray close(pout[0]); 225511b41d2SMark Murray if (dup2(pout[1], 1) < 0) 226511b41d2SMark Murray perror("dup2 stdout"); 227511b41d2SMark Murray /* Cannot be 1 because pin allocated two descriptors. */ 228511b41d2SMark Murray close(pout[1]); 229511b41d2SMark Murray 230511b41d2SMark Murray /* Stderr is left as it is so that error messages get 231511b41d2SMark Murray printed on the user's terminal. */ 232d4af9e69SDag-Erling Smørgrav argv[0] = shell; 233511b41d2SMark Murray argv[1] = "-c"; 234511b41d2SMark Murray argv[2] = command_string; 235511b41d2SMark Murray argv[3] = NULL; 236511b41d2SMark Murray 237511b41d2SMark Murray /* Execute the proxy command. Note that we gave up any 238511b41d2SMark Murray extra privileges above. */ 2394a421b63SDag-Erling Smørgrav signal(SIGPIPE, SIG_DFL); 240ca3176e7SBrian Feldman execv(argv[0], argv); 241ca3176e7SBrian Feldman perror(argv[0]); 242511b41d2SMark Murray exit(1); 243511b41d2SMark Murray } 244511b41d2SMark Murray /* Parent. */ 245511b41d2SMark Murray if (pid < 0) 246511b41d2SMark Murray fatal("fork failed: %.100s", strerror(errno)); 247f388f5efSDag-Erling Smørgrav else 248f388f5efSDag-Erling Smørgrav proxy_command_pid = pid; /* save pid to clean up later */ 249511b41d2SMark Murray 250511b41d2SMark Murray /* Close child side of the descriptors. */ 251511b41d2SMark Murray close(pin[0]); 252511b41d2SMark Murray close(pout[1]); 253511b41d2SMark Murray 254511b41d2SMark Murray /* Free the command name. */ 255e4a9863fSDag-Erling Smørgrav free(command_string); 256511b41d2SMark Murray 257511b41d2SMark Murray /* Set the connection file descriptors. */ 258*4f52dfbbSDag-Erling Smørgrav if (ssh_packet_set_connection(ssh, pout[0], pin[1]) == NULL) 259*4f52dfbbSDag-Erling Smørgrav return -1; /* ssh_packet_set_connection logs error */ 260511b41d2SMark Murray 261af12a3e7SDag-Erling Smørgrav return 0; 262511b41d2SMark Murray } 263511b41d2SMark Murray 2644a421b63SDag-Erling Smørgrav void 2654a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(void) 2664a421b63SDag-Erling Smørgrav { 2674a421b63SDag-Erling Smørgrav /* 2684a421b63SDag-Erling Smørgrav * Send SIGHUP to proxy command if used. We don't wait() in 2694a421b63SDag-Erling Smørgrav * case it hangs and instead rely on init to reap the child 2704a421b63SDag-Erling Smørgrav */ 2714a421b63SDag-Erling Smørgrav if (proxy_command_pid > 1) 2724a421b63SDag-Erling Smørgrav kill(proxy_command_pid, SIGHUP); 2734a421b63SDag-Erling Smørgrav } 2744a421b63SDag-Erling Smørgrav 275511b41d2SMark Murray /* 276511b41d2SMark Murray * Creates a (possibly privileged) socket for use as the ssh connection. 277511b41d2SMark Murray */ 278af12a3e7SDag-Erling Smørgrav static int 279cf2b5f3bSDag-Erling Smørgrav ssh_create_socket(int privileged, struct addrinfo *ai) 280511b41d2SMark Murray { 281f7167e0eSDag-Erling Smørgrav int sock, r, gaierr; 282b83788ffSDag-Erling Smørgrav struct addrinfo hints, *res = NULL; 283511b41d2SMark Murray 284cf2b5f3bSDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 285b15c8340SDag-Erling Smørgrav if (sock < 0) { 286f7167e0eSDag-Erling Smørgrav error("socket: %s", strerror(errno)); 287b15c8340SDag-Erling Smørgrav return -1; 288b15c8340SDag-Erling Smørgrav } 289b15c8340SDag-Erling Smørgrav fcntl(sock, F_SETFD, FD_CLOEXEC); 290af12a3e7SDag-Erling Smørgrav 291af12a3e7SDag-Erling Smørgrav /* Bind the socket to an alternative local IP address */ 292f7167e0eSDag-Erling Smørgrav if (options.bind_address == NULL && !privileged) 293af12a3e7SDag-Erling Smørgrav return sock; 294af12a3e7SDag-Erling Smørgrav 295b83788ffSDag-Erling Smørgrav if (options.bind_address) { 296af12a3e7SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 297cf2b5f3bSDag-Erling Smørgrav hints.ai_family = ai->ai_family; 298cf2b5f3bSDag-Erling Smørgrav hints.ai_socktype = ai->ai_socktype; 299cf2b5f3bSDag-Erling Smørgrav hints.ai_protocol = ai->ai_protocol; 300af12a3e7SDag-Erling Smørgrav hints.ai_flags = AI_PASSIVE; 301d4af9e69SDag-Erling Smørgrav gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); 302af12a3e7SDag-Erling Smørgrav if (gaierr) { 303af12a3e7SDag-Erling Smørgrav error("getaddrinfo: %s: %s", options.bind_address, 304d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 305af12a3e7SDag-Erling Smørgrav close(sock); 306af12a3e7SDag-Erling Smørgrav return -1; 307511b41d2SMark Murray } 308b83788ffSDag-Erling Smørgrav } 309f7167e0eSDag-Erling Smørgrav /* 310f7167e0eSDag-Erling Smørgrav * If we are running as root and want to connect to a privileged 311f7167e0eSDag-Erling Smørgrav * port, bind our own socket to a privileged port. 312f7167e0eSDag-Erling Smørgrav */ 313f7167e0eSDag-Erling Smørgrav if (privileged) { 314f7167e0eSDag-Erling Smørgrav PRIV_START; 315b83788ffSDag-Erling Smørgrav r = bindresvport_sa(sock, res ? res->ai_addr : NULL); 316f7167e0eSDag-Erling Smørgrav PRIV_END; 317f7167e0eSDag-Erling Smørgrav if (r < 0) { 318f7167e0eSDag-Erling Smørgrav error("bindresvport_sa: af=%d %s", ai->ai_family, 319f7167e0eSDag-Erling Smørgrav strerror(errno)); 320f7167e0eSDag-Erling Smørgrav goto fail; 321f7167e0eSDag-Erling Smørgrav } 322f7167e0eSDag-Erling Smørgrav } else { 323af12a3e7SDag-Erling Smørgrav if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 324f7167e0eSDag-Erling Smørgrav error("bind: %s: %s", options.bind_address, 325f7167e0eSDag-Erling Smørgrav strerror(errno)); 326f7167e0eSDag-Erling Smørgrav fail: 327af12a3e7SDag-Erling Smørgrav close(sock); 328af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 329af12a3e7SDag-Erling Smørgrav return -1; 330af12a3e7SDag-Erling Smørgrav } 331f7167e0eSDag-Erling Smørgrav } 332b83788ffSDag-Erling Smørgrav if (res != NULL) 333af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 334511b41d2SMark Murray return sock; 335511b41d2SMark Murray } 336511b41d2SMark Murray 337*4f52dfbbSDag-Erling Smørgrav /* 338*4f52dfbbSDag-Erling Smørgrav * Wait up to *timeoutp milliseconds for fd to be readable. Updates 339*4f52dfbbSDag-Erling Smørgrav * *timeoutp with time remaining. 340*4f52dfbbSDag-Erling Smørgrav * Returns 0 if fd ready or -1 on timeout or error (see errno). 341*4f52dfbbSDag-Erling Smørgrav */ 342*4f52dfbbSDag-Erling Smørgrav static int 343*4f52dfbbSDag-Erling Smørgrav waitrfd(int fd, int *timeoutp) 344*4f52dfbbSDag-Erling Smørgrav { 345*4f52dfbbSDag-Erling Smørgrav struct pollfd pfd; 346*4f52dfbbSDag-Erling Smørgrav struct timeval t_start; 347*4f52dfbbSDag-Erling Smørgrav int oerrno, r; 348*4f52dfbbSDag-Erling Smørgrav 349*4f52dfbbSDag-Erling Smørgrav gettimeofday(&t_start, NULL); 350*4f52dfbbSDag-Erling Smørgrav pfd.fd = fd; 351*4f52dfbbSDag-Erling Smørgrav pfd.events = POLLIN; 352*4f52dfbbSDag-Erling Smørgrav for (; *timeoutp >= 0;) { 353*4f52dfbbSDag-Erling Smørgrav r = poll(&pfd, 1, *timeoutp); 354*4f52dfbbSDag-Erling Smørgrav oerrno = errno; 355*4f52dfbbSDag-Erling Smørgrav ms_subtract_diff(&t_start, timeoutp); 356*4f52dfbbSDag-Erling Smørgrav errno = oerrno; 357*4f52dfbbSDag-Erling Smørgrav if (r > 0) 358*4f52dfbbSDag-Erling Smørgrav return 0; 359*4f52dfbbSDag-Erling Smørgrav else if (r == -1 && errno != EAGAIN) 360*4f52dfbbSDag-Erling Smørgrav return -1; 361*4f52dfbbSDag-Erling Smørgrav else if (r == 0) 362*4f52dfbbSDag-Erling Smørgrav break; 363*4f52dfbbSDag-Erling Smørgrav } 364*4f52dfbbSDag-Erling Smørgrav /* timeout */ 365*4f52dfbbSDag-Erling Smørgrav errno = ETIMEDOUT; 366*4f52dfbbSDag-Erling Smørgrav return -1; 367*4f52dfbbSDag-Erling Smørgrav } 368*4f52dfbbSDag-Erling Smørgrav 369cf2b5f3bSDag-Erling Smørgrav static int 370cf2b5f3bSDag-Erling Smørgrav timeout_connect(int sockfd, const struct sockaddr *serv_addr, 371d4af9e69SDag-Erling Smørgrav socklen_t addrlen, int *timeoutp) 372cf2b5f3bSDag-Erling Smørgrav { 373*4f52dfbbSDag-Erling Smørgrav int optval = 0; 374*4f52dfbbSDag-Erling Smørgrav socklen_t optlen = sizeof(optval); 375cf2b5f3bSDag-Erling Smørgrav 376*4f52dfbbSDag-Erling Smørgrav /* No timeout: just do a blocking connect() */ 377*4f52dfbbSDag-Erling Smørgrav if (*timeoutp <= 0) 378*4f52dfbbSDag-Erling Smørgrav return connect(sockfd, serv_addr, addrlen); 379cf2b5f3bSDag-Erling Smørgrav 3801ec0d754SDag-Erling Smørgrav set_nonblock(sockfd); 381*4f52dfbbSDag-Erling Smørgrav if (connect(sockfd, serv_addr, addrlen) == 0) { 382*4f52dfbbSDag-Erling Smørgrav /* Succeeded already? */ 3831ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 384*4f52dfbbSDag-Erling Smørgrav return 0; 385*4f52dfbbSDag-Erling Smørgrav } else if (errno != EINPROGRESS) 386*4f52dfbbSDag-Erling Smørgrav return -1; 387cf2b5f3bSDag-Erling Smørgrav 388*4f52dfbbSDag-Erling Smørgrav if (waitrfd(sockfd, timeoutp) == -1) 389*4f52dfbbSDag-Erling Smørgrav return -1; 390cf2b5f3bSDag-Erling Smørgrav 391cf2b5f3bSDag-Erling Smørgrav /* Completed or failed */ 392*4f52dfbbSDag-Erling Smørgrav if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { 393cf2b5f3bSDag-Erling Smørgrav debug("getsockopt: %s", strerror(errno)); 394*4f52dfbbSDag-Erling Smørgrav return -1; 395cf2b5f3bSDag-Erling Smørgrav } 396cf2b5f3bSDag-Erling Smørgrav if (optval != 0) { 397cf2b5f3bSDag-Erling Smørgrav errno = optval; 398*4f52dfbbSDag-Erling Smørgrav return -1; 399cf2b5f3bSDag-Erling Smørgrav } 4001ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 401*4f52dfbbSDag-Erling Smørgrav return 0; 402cf2b5f3bSDag-Erling Smørgrav } 403cf2b5f3bSDag-Erling Smørgrav 404511b41d2SMark Murray /* 405511b41d2SMark Murray * Opens a TCP/IP connection to the remote server on the given host. 406511b41d2SMark Murray * The address of the remote host will be returned in hostaddr. 40780628bacSDag-Erling Smørgrav * If port is 0, the default port will be used. If needpriv is true, 408511b41d2SMark Murray * a privileged port will be allocated to make the connection. 40980628bacSDag-Erling Smørgrav * This requires super-user privileges if needpriv is true. 410511b41d2SMark Murray * Connection_attempts specifies the maximum number of tries (one per 411511b41d2SMark Murray * second). If proxy_command is non-NULL, it specifies the command (with %h 412511b41d2SMark Murray * and %p substituted for host and port, respectively) to use to contact 413511b41d2SMark Murray * the daemon. 414511b41d2SMark Murray */ 415f7167e0eSDag-Erling Smørgrav static int 416*4f52dfbbSDag-Erling Smørgrav ssh_connect_direct(struct ssh *ssh, const char *host, struct addrinfo *aitop, 417f7167e0eSDag-Erling Smørgrav struct sockaddr_storage *hostaddr, u_short port, int family, 418f7167e0eSDag-Erling Smørgrav int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) 419511b41d2SMark Murray { 420ca3176e7SBrian Feldman int on = 1; 421ca3176e7SBrian Feldman int sock = -1, attempt; 422ca3176e7SBrian Feldman char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 423f7167e0eSDag-Erling Smørgrav struct addrinfo *ai; 424511b41d2SMark Murray 425acc1a9efSDag-Erling Smørgrav debug2("%s: needpriv %d", __func__, needpriv); 426acc1a9efSDag-Erling Smørgrav memset(ntop, 0, sizeof(ntop)); 427acc1a9efSDag-Erling Smørgrav memset(strport, 0, sizeof(strport)); 428511b41d2SMark Murray 429333ee039SDag-Erling Smørgrav for (attempt = 0; attempt < connection_attempts; attempt++) { 43062efe23aSDag-Erling Smørgrav if (attempt > 0) { 43162efe23aSDag-Erling Smørgrav /* Sleep a moment before retrying. */ 43262efe23aSDag-Erling Smørgrav sleep(1); 433511b41d2SMark Murray debug("Trying again..."); 43462efe23aSDag-Erling Smørgrav } 435333ee039SDag-Erling Smørgrav /* 436333ee039SDag-Erling Smørgrav * Loop through addresses for this host, and try each one in 437333ee039SDag-Erling Smørgrav * sequence until the connection succeeds. 438333ee039SDag-Erling Smørgrav */ 439511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 440f7167e0eSDag-Erling Smørgrav if (ai->ai_family != AF_INET && 441f7167e0eSDag-Erling Smørgrav ai->ai_family != AF_INET6) 442511b41d2SMark Murray continue; 443511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 444511b41d2SMark Murray ntop, sizeof(ntop), strport, sizeof(strport), 445511b41d2SMark Murray NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 446acc1a9efSDag-Erling Smørgrav error("%s: getnameinfo failed", __func__); 447511b41d2SMark Murray continue; 448511b41d2SMark Murray } 449511b41d2SMark Murray debug("Connecting to %.200s [%.100s] port %s.", 4501f5ce8f4SBrian Feldman host, ntop, strport); 451511b41d2SMark Murray 452511b41d2SMark Murray /* Create a socket for connecting. */ 453cf2b5f3bSDag-Erling Smørgrav sock = ssh_create_socket(needpriv, ai); 454511b41d2SMark Murray if (sock < 0) 455af12a3e7SDag-Erling Smørgrav /* Any error is already output */ 456511b41d2SMark Murray continue; 457511b41d2SMark Murray 458cf2b5f3bSDag-Erling Smørgrav if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 459d4af9e69SDag-Erling Smørgrav timeout_ms) >= 0) { 460511b41d2SMark Murray /* Successful connection. */ 461c322fe35SKris Kennaway memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 462511b41d2SMark Murray break; 463511b41d2SMark Murray } else { 464f388f5efSDag-Erling Smørgrav debug("connect to address %s port %s: %s", 465f388f5efSDag-Erling Smørgrav ntop, strport, strerror(errno)); 466511b41d2SMark Murray close(sock); 467333ee039SDag-Erling Smørgrav sock = -1; 468511b41d2SMark Murray } 469511b41d2SMark Murray } 470333ee039SDag-Erling Smørgrav if (sock != -1) 471511b41d2SMark Murray break; /* Successful connection. */ 472511b41d2SMark Murray } 473511b41d2SMark Murray 474511b41d2SMark Murray /* Return failure if we didn't get a successful connection. */ 475333ee039SDag-Erling Smørgrav if (sock == -1) { 476aa49c926SDag-Erling Smørgrav error("ssh: connect to host %s port %s: %s", 477f388f5efSDag-Erling Smørgrav host, strport, strerror(errno)); 478aa49c926SDag-Erling Smørgrav return (-1); 479f388f5efSDag-Erling Smørgrav } 480511b41d2SMark Murray 481511b41d2SMark Murray debug("Connection established."); 482511b41d2SMark Murray 4831ec0d754SDag-Erling Smørgrav /* Set SO_KEEPALIVE if requested. */ 484d4af9e69SDag-Erling Smørgrav if (want_keepalive && 485ca3176e7SBrian Feldman setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 486ca3176e7SBrian Feldman sizeof(on)) < 0) 487ca3176e7SBrian Feldman error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 488ca3176e7SBrian Feldman 489511b41d2SMark Murray /* Set the connection. */ 490*4f52dfbbSDag-Erling Smørgrav if (ssh_packet_set_connection(ssh, sock, sock) == NULL) 491*4f52dfbbSDag-Erling Smørgrav return -1; /* ssh_packet_set_connection logs error */ 492511b41d2SMark Murray 493af12a3e7SDag-Erling Smørgrav return 0; 494511b41d2SMark Murray } 495511b41d2SMark Murray 496f7167e0eSDag-Erling Smørgrav int 497*4f52dfbbSDag-Erling Smørgrav ssh_connect(struct ssh *ssh, const char *host, struct addrinfo *addrs, 498f7167e0eSDag-Erling Smørgrav struct sockaddr_storage *hostaddr, u_short port, int family, 499f7167e0eSDag-Erling Smørgrav int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) 500f7167e0eSDag-Erling Smørgrav { 501f7167e0eSDag-Erling Smørgrav if (options.proxy_command == NULL) { 502*4f52dfbbSDag-Erling Smørgrav return ssh_connect_direct(ssh, host, addrs, hostaddr, port, 503*4f52dfbbSDag-Erling Smørgrav family, connection_attempts, timeout_ms, want_keepalive, 504*4f52dfbbSDag-Erling Smørgrav needpriv); 505f7167e0eSDag-Erling Smørgrav } else if (strcmp(options.proxy_command, "-") == 0) { 506*4f52dfbbSDag-Erling Smørgrav if ((ssh_packet_set_connection(ssh, 507*4f52dfbbSDag-Erling Smørgrav STDIN_FILENO, STDOUT_FILENO)) == NULL) 508*4f52dfbbSDag-Erling Smørgrav return -1; /* ssh_packet_set_connection logs error */ 509*4f52dfbbSDag-Erling Smørgrav return 0; 510f7167e0eSDag-Erling Smørgrav } else if (options.proxy_use_fdpass) { 511*4f52dfbbSDag-Erling Smørgrav return ssh_proxy_fdpass_connect(ssh, host, port, 512f7167e0eSDag-Erling Smørgrav options.proxy_command); 513f7167e0eSDag-Erling Smørgrav } 514*4f52dfbbSDag-Erling Smørgrav return ssh_proxy_connect(ssh, host, port, options.proxy_command); 515f7167e0eSDag-Erling Smørgrav } 516f7167e0eSDag-Erling Smørgrav 5176888a9beSDag-Erling Smørgrav static void 5186888a9beSDag-Erling Smørgrav send_client_banner(int connection_out, int minor1) 5196888a9beSDag-Erling Smørgrav { 5206888a9beSDag-Erling Smørgrav /* Send our own protocol version identification. */ 521*4f52dfbbSDag-Erling Smørgrav xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s\n", 522*4f52dfbbSDag-Erling Smørgrav PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, 5236888a9beSDag-Erling Smørgrav *options.version_addendum == '\0' ? "" : " ", 524*4f52dfbbSDag-Erling Smørgrav options.version_addendum); 525acc1a9efSDag-Erling Smørgrav if (atomicio(vwrite, connection_out, client_version_string, 5266888a9beSDag-Erling Smørgrav strlen(client_version_string)) != strlen(client_version_string)) 5276888a9beSDag-Erling Smørgrav fatal("write: %.100s", strerror(errno)); 5286888a9beSDag-Erling Smørgrav chop(client_version_string); 5296888a9beSDag-Erling Smørgrav debug("Local version string %.100s", client_version_string); 5306888a9beSDag-Erling Smørgrav } 5316888a9beSDag-Erling Smørgrav 532511b41d2SMark Murray /* 533e8aafc91SKris Kennaway * Waits for the server identification string, and sends our own 534e8aafc91SKris Kennaway * identification string. 535511b41d2SMark Murray */ 5367aee6ffeSDag-Erling Smørgrav void 537d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(int timeout_ms) 538511b41d2SMark Murray { 539e8aafc91SKris Kennaway char buf[256], remote_version[256]; /* must be same size! */ 540d4ecd108SDag-Erling Smørgrav int remote_major, remote_minor, mismatch; 541e8aafc91SKris Kennaway int connection_in = packet_get_connection_in(); 542e8aafc91SKris Kennaway int connection_out = packet_get_connection_out(); 543333ee039SDag-Erling Smørgrav u_int i, n; 544d4af9e69SDag-Erling Smørgrav size_t len; 545*4f52dfbbSDag-Erling Smørgrav int rc; 546d4af9e69SDag-Erling Smørgrav 5476888a9beSDag-Erling Smørgrav send_client_banner(connection_out, 0); 5486888a9beSDag-Erling Smørgrav 549d4ecd108SDag-Erling Smørgrav /* Read other side's version identification. */ 550333ee039SDag-Erling Smørgrav for (n = 0;;) { 551e8aafc91SKris Kennaway for (i = 0; i < sizeof(buf) - 1; i++) { 552d4af9e69SDag-Erling Smørgrav if (timeout_ms > 0) { 553*4f52dfbbSDag-Erling Smørgrav rc = waitrfd(connection_in, &timeout_ms); 554*4f52dfbbSDag-Erling Smørgrav if (rc == -1 && errno == ETIMEDOUT) { 555d4af9e69SDag-Erling Smørgrav fatal("Connection timed out during " 556d4af9e69SDag-Erling Smørgrav "banner exchange"); 557*4f52dfbbSDag-Erling Smørgrav } else if (rc == -1) { 558*4f52dfbbSDag-Erling Smørgrav fatal("%s: %s", 559*4f52dfbbSDag-Erling Smørgrav __func__, strerror(errno)); 560d4af9e69SDag-Erling Smørgrav } 561d4af9e69SDag-Erling Smørgrav } 562d4af9e69SDag-Erling Smørgrav 563acc1a9efSDag-Erling Smørgrav len = atomicio(read, connection_in, &buf[i], 1); 564d4ecd108SDag-Erling Smørgrav if (len != 1 && errno == EPIPE) 565d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 566d4af9e69SDag-Erling Smørgrav "Connection closed by remote host"); 567d4ecd108SDag-Erling Smørgrav else if (len != 1) 568d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 569d4af9e69SDag-Erling Smørgrav "read: %.100s", strerror(errno)); 570e8aafc91SKris Kennaway if (buf[i] == '\r') { 571e8aafc91SKris Kennaway buf[i] = '\n'; 572e8aafc91SKris Kennaway buf[i + 1] = 0; 573e8aafc91SKris Kennaway continue; /**XXX wait for \n */ 574511b41d2SMark Murray } 575e8aafc91SKris Kennaway if (buf[i] == '\n') { 576e8aafc91SKris Kennaway buf[i + 1] = 0; 577511b41d2SMark Murray break; 578e8aafc91SKris Kennaway } 579333ee039SDag-Erling Smørgrav if (++n > 65536) 580d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 581d4af9e69SDag-Erling Smørgrav "No banner received"); 582e8aafc91SKris Kennaway } 583e8aafc91SKris Kennaway buf[sizeof(buf) - 1] = 0; 584c2d3a559SKris Kennaway if (strncmp(buf, "SSH-", 4) == 0) 585c2d3a559SKris Kennaway break; 586c2d3a559SKris Kennaway debug("ssh_exchange_identification: %s", buf); 587c2d3a559SKris Kennaway } 588e8aafc91SKris Kennaway server_version_string = xstrdup(buf); 589511b41d2SMark Murray 590511b41d2SMark Murray /* 591e8aafc91SKris Kennaway * Check that the versions match. In future this might accept 592e8aafc91SKris Kennaway * several versions and set appropriate flags to handle them. 593511b41d2SMark Murray */ 594e8aafc91SKris Kennaway if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 595e8aafc91SKris Kennaway &remote_major, &remote_minor, remote_version) != 3) 596e8aafc91SKris Kennaway fatal("Bad remote protocol version identification: '%.100s'", buf); 597e8aafc91SKris Kennaway debug("Remote protocol version %d.%d, remote software version %.100s", 598e8aafc91SKris Kennaway remote_major, remote_minor, remote_version); 599511b41d2SMark Murray 600bc5531deSDag-Erling Smørgrav active_state->compat = compat_datafellows(remote_version); 601e8aafc91SKris Kennaway mismatch = 0; 602e8aafc91SKris Kennaway 603e8aafc91SKris Kennaway switch (remote_major) { 604*4f52dfbbSDag-Erling Smørgrav case 2: 605511b41d2SMark Murray break; 606*4f52dfbbSDag-Erling Smørgrav case 1: 607*4f52dfbbSDag-Erling Smørgrav if (remote_minor != 99) 608e8aafc91SKris Kennaway mismatch = 1; 609e8aafc91SKris Kennaway break; 610511b41d2SMark Murray default: 611e8aafc91SKris Kennaway mismatch = 1; 612e8aafc91SKris Kennaway break; 613511b41d2SMark Murray } 614e8aafc91SKris Kennaway if (mismatch) 615e8aafc91SKris Kennaway fatal("Protocol major versions differ: %d vs. %d", 616*4f52dfbbSDag-Erling Smørgrav PROTOCOL_MAJOR_2, remote_major); 617f7167e0eSDag-Erling Smørgrav if ((datafellows & SSH_BUG_DERIVEKEY) != 0) 618f7167e0eSDag-Erling Smørgrav fatal("Server version \"%.100s\" uses unsafe key agreement; " 619f7167e0eSDag-Erling Smørgrav "refusing connection", remote_version); 620f7167e0eSDag-Erling Smørgrav if ((datafellows & SSH_BUG_RSASIGMD5) != 0) 621f7167e0eSDag-Erling Smørgrav logit("Server version \"%.100s\" uses unsafe RSA signature " 622f7167e0eSDag-Erling Smørgrav "scheme; disabling use of RSA keys", remote_version); 623e8aafc91SKris Kennaway chop(server_version_string); 624511b41d2SMark Murray } 625511b41d2SMark Murray 626ca3176e7SBrian Feldman /* defaults to 'no' */ 627af12a3e7SDag-Erling Smørgrav static int 628af12a3e7SDag-Erling Smørgrav confirm(const char *prompt) 629511b41d2SMark Murray { 630af12a3e7SDag-Erling Smørgrav const char *msg, *again = "Please type 'yes' or 'no': "; 631af12a3e7SDag-Erling Smørgrav char *p; 632af12a3e7SDag-Erling Smørgrav int ret = -1; 633511b41d2SMark Murray 634ca3176e7SBrian Feldman if (options.batch_mode) 635ca3176e7SBrian Feldman return 0; 636af12a3e7SDag-Erling Smørgrav for (msg = prompt;;msg = again) { 637af12a3e7SDag-Erling Smørgrav p = read_passphrase(msg, RP_ECHO); 638af12a3e7SDag-Erling Smørgrav if (p == NULL || 639af12a3e7SDag-Erling Smørgrav (p[0] == '\0') || (p[0] == '\n') || 640af12a3e7SDag-Erling Smørgrav strncasecmp(p, "no", 2) == 0) 641af12a3e7SDag-Erling Smørgrav ret = 0; 642f388f5efSDag-Erling Smørgrav if (p && strncasecmp(p, "yes", 3) == 0) 643af12a3e7SDag-Erling Smørgrav ret = 1; 644e4a9863fSDag-Erling Smørgrav free(p); 645af12a3e7SDag-Erling Smørgrav if (ret != -1) 646af12a3e7SDag-Erling Smørgrav return ret; 647511b41d2SMark Murray } 648511b41d2SMark Murray } 649511b41d2SMark Murray 650b15c8340SDag-Erling Smørgrav static int 651*4f52dfbbSDag-Erling Smørgrav check_host_cert(const char *host, const struct sshkey *host_key) 652b15c8340SDag-Erling Smørgrav { 653b15c8340SDag-Erling Smørgrav const char *reason; 654b15c8340SDag-Erling Smørgrav 655b15c8340SDag-Erling Smørgrav if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { 656b15c8340SDag-Erling Smørgrav error("%s", reason); 657b15c8340SDag-Erling Smørgrav return 0; 658b15c8340SDag-Erling Smørgrav } 659a0ee8cc6SDag-Erling Smørgrav if (buffer_len(host_key->cert->critical) != 0) { 660e2f6069cSDag-Erling Smørgrav error("Certificate for %s contains unsupported " 661e2f6069cSDag-Erling Smørgrav "critical options(s)", host); 662b15c8340SDag-Erling Smørgrav return 0; 663b15c8340SDag-Erling Smørgrav } 664b15c8340SDag-Erling Smørgrav return 1; 665b15c8340SDag-Erling Smørgrav } 666b15c8340SDag-Erling Smørgrav 6674a421b63SDag-Erling Smørgrav static int 6684a421b63SDag-Erling Smørgrav sockaddr_is_local(struct sockaddr *hostaddr) 6694a421b63SDag-Erling Smørgrav { 6704a421b63SDag-Erling Smørgrav switch (hostaddr->sa_family) { 6714a421b63SDag-Erling Smørgrav case AF_INET: 6724a421b63SDag-Erling Smørgrav return (ntohl(((struct sockaddr_in *)hostaddr)-> 6734a421b63SDag-Erling Smørgrav sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 6744a421b63SDag-Erling Smørgrav case AF_INET6: 6754a421b63SDag-Erling Smørgrav return IN6_IS_ADDR_LOOPBACK( 6764a421b63SDag-Erling Smørgrav &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 6774a421b63SDag-Erling Smørgrav default: 6784a421b63SDag-Erling Smørgrav return 0; 6794a421b63SDag-Erling Smørgrav } 6804a421b63SDag-Erling Smørgrav } 6814a421b63SDag-Erling Smørgrav 6824a421b63SDag-Erling Smørgrav /* 6834a421b63SDag-Erling Smørgrav * Prepare the hostname and ip address strings that are used to lookup 6844a421b63SDag-Erling Smørgrav * host keys in known_hosts files. These may have a port number appended. 6854a421b63SDag-Erling Smørgrav */ 6864a421b63SDag-Erling Smørgrav void 6874a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, 6884a421b63SDag-Erling Smørgrav u_short port, char **hostfile_hostname, char **hostfile_ipaddr) 6894a421b63SDag-Erling Smørgrav { 6904a421b63SDag-Erling Smørgrav char ntop[NI_MAXHOST]; 6914a421b63SDag-Erling Smørgrav socklen_t addrlen; 6924a421b63SDag-Erling Smørgrav 6934a421b63SDag-Erling Smørgrav switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { 6944a421b63SDag-Erling Smørgrav case -1: 6954a421b63SDag-Erling Smørgrav addrlen = 0; 6964a421b63SDag-Erling Smørgrav break; 6974a421b63SDag-Erling Smørgrav case AF_INET: 6984a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr_in); 6994a421b63SDag-Erling Smørgrav break; 7004a421b63SDag-Erling Smørgrav case AF_INET6: 7014a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr_in6); 7024a421b63SDag-Erling Smørgrav break; 7034a421b63SDag-Erling Smørgrav default: 7044a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr); 7054a421b63SDag-Erling Smørgrav break; 7064a421b63SDag-Erling Smørgrav } 7074a421b63SDag-Erling Smørgrav 7084a421b63SDag-Erling Smørgrav /* 7094a421b63SDag-Erling Smørgrav * We don't have the remote ip-address for connections 7104a421b63SDag-Erling Smørgrav * using a proxy command 7114a421b63SDag-Erling Smørgrav */ 7124a421b63SDag-Erling Smørgrav if (hostfile_ipaddr != NULL) { 7134a421b63SDag-Erling Smørgrav if (options.proxy_command == NULL) { 7144a421b63SDag-Erling Smørgrav if (getnameinfo(hostaddr, addrlen, 7154a421b63SDag-Erling Smørgrav ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) 716bc5531deSDag-Erling Smørgrav fatal("%s: getnameinfo failed", __func__); 7174a421b63SDag-Erling Smørgrav *hostfile_ipaddr = put_host_port(ntop, port); 7184a421b63SDag-Erling Smørgrav } else { 7194a421b63SDag-Erling Smørgrav *hostfile_ipaddr = xstrdup("<no hostip for proxy " 7204a421b63SDag-Erling Smørgrav "command>"); 7214a421b63SDag-Erling Smørgrav } 7224a421b63SDag-Erling Smørgrav } 7234a421b63SDag-Erling Smørgrav 7244a421b63SDag-Erling Smørgrav /* 7254a421b63SDag-Erling Smørgrav * Allow the user to record the key under a different name or 7264a421b63SDag-Erling Smørgrav * differentiate a non-standard port. This is useful for ssh 7274a421b63SDag-Erling Smørgrav * tunneling over forwarded connections or if you run multiple 7284a421b63SDag-Erling Smørgrav * sshd's on different ports on the same machine. 7294a421b63SDag-Erling Smørgrav */ 7304a421b63SDag-Erling Smørgrav if (hostfile_hostname != NULL) { 7314a421b63SDag-Erling Smørgrav if (options.host_key_alias != NULL) { 7324a421b63SDag-Erling Smørgrav *hostfile_hostname = xstrdup(options.host_key_alias); 7334a421b63SDag-Erling Smørgrav debug("using hostkeyalias: %s", *hostfile_hostname); 7344a421b63SDag-Erling Smørgrav } else { 7354a421b63SDag-Erling Smørgrav *hostfile_hostname = put_host_port(hostname, port); 7364a421b63SDag-Erling Smørgrav } 7374a421b63SDag-Erling Smørgrav } 7384a421b63SDag-Erling Smørgrav } 7394a421b63SDag-Erling Smørgrav 740e8aafc91SKris Kennaway /* 741af12a3e7SDag-Erling Smørgrav * check whether the supplied host key is valid, return -1 if the key 742e146993eSDag-Erling Smørgrav * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. 743e8aafc91SKris Kennaway */ 744333ee039SDag-Erling Smørgrav #define RDRW 0 745333ee039SDag-Erling Smørgrav #define RDONLY 1 746333ee039SDag-Erling Smørgrav #define ROQUIET 2 747af12a3e7SDag-Erling Smørgrav static int 748333ee039SDag-Erling Smørgrav check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 749*4f52dfbbSDag-Erling Smørgrav struct sshkey *host_key, int readonly, 750e146993eSDag-Erling Smørgrav char **user_hostfiles, u_int num_user_hostfiles, 751e146993eSDag-Erling Smørgrav char **system_hostfiles, u_int num_system_hostfiles) 752511b41d2SMark Murray { 753e8aafc91SKris Kennaway HostStatus host_status; 754e8aafc91SKris Kennaway HostStatus ip_status; 755*4f52dfbbSDag-Erling Smørgrav struct sshkey *raw_key = NULL; 756e146993eSDag-Erling Smørgrav char *ip = NULL, *host = NULL; 757e146993eSDag-Erling Smørgrav char hostline[1000], *hostp, *fp, *ra; 758af12a3e7SDag-Erling Smørgrav char msg[1024]; 759e146993eSDag-Erling Smørgrav const char *type; 7604a421b63SDag-Erling Smørgrav const struct hostkey_entry *host_found, *ip_found; 761e146993eSDag-Erling Smørgrav int len, cancelled_forwarding = 0; 762e146993eSDag-Erling Smørgrav int local = sockaddr_is_local(hostaddr); 763*4f52dfbbSDag-Erling Smørgrav int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0; 764bc5531deSDag-Erling Smørgrav int hostkey_trusted = 0; /* Known or explicitly accepted by user */ 765e146993eSDag-Erling Smørgrav struct hostkeys *host_hostkeys, *ip_hostkeys; 766e146993eSDag-Erling Smørgrav u_int i; 767511b41d2SMark Murray 768e8aafc91SKris Kennaway /* 769e8aafc91SKris Kennaway * Force accepting of the host key for loopback/localhost. The 770e8aafc91SKris Kennaway * problem is that if the home directory is NFS-mounted to multiple 771e8aafc91SKris Kennaway * machines, localhost will refer to a different machine in each of 772e8aafc91SKris Kennaway * them, and the user will get bogus HOST_CHANGED warnings. This 773e8aafc91SKris Kennaway * essentially disables host authentication for localhost; however, 774e8aafc91SKris Kennaway * this is probably not a real problem. 775e8aafc91SKris Kennaway */ 776af12a3e7SDag-Erling Smørgrav if (options.no_host_authentication_for_localhost == 1 && local && 777af12a3e7SDag-Erling Smørgrav options.host_key_alias == NULL) { 778ca3176e7SBrian Feldman debug("Forcing accepting of host key for " 779ca3176e7SBrian Feldman "loopback/localhost."); 780af12a3e7SDag-Erling Smørgrav return 0; 781511b41d2SMark Murray } 782511b41d2SMark Murray 783e8aafc91SKris Kennaway /* 7844a421b63SDag-Erling Smørgrav * Prepare the hostname and address strings used for hostkey lookup. 7854a421b63SDag-Erling Smørgrav * In some cases, these will have a port number appended. 786e8aafc91SKris Kennaway */ 7874a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); 788d4af9e69SDag-Erling Smørgrav 789ca3176e7SBrian Feldman /* 790ca3176e7SBrian Feldman * Turn off check_host_ip if the connection is to localhost, via proxy 791ca3176e7SBrian Feldman * command or if we don't have a hostname to compare with 792ca3176e7SBrian Feldman */ 793333ee039SDag-Erling Smørgrav if (options.check_host_ip && (local || 794333ee039SDag-Erling Smørgrav strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 795ca3176e7SBrian Feldman options.check_host_ip = 0; 796ca3176e7SBrian Feldman 7974a421b63SDag-Erling Smørgrav host_hostkeys = init_hostkeys(); 798e146993eSDag-Erling Smørgrav for (i = 0; i < num_user_hostfiles; i++) 799e146993eSDag-Erling Smørgrav load_hostkeys(host_hostkeys, host, user_hostfiles[i]); 800e146993eSDag-Erling Smørgrav for (i = 0; i < num_system_hostfiles; i++) 801e146993eSDag-Erling Smørgrav load_hostkeys(host_hostkeys, host, system_hostfiles[i]); 8024a421b63SDag-Erling Smørgrav 8034a421b63SDag-Erling Smørgrav ip_hostkeys = NULL; 8044a421b63SDag-Erling Smørgrav if (!want_cert && options.check_host_ip) { 8054a421b63SDag-Erling Smørgrav ip_hostkeys = init_hostkeys(); 806e146993eSDag-Erling Smørgrav for (i = 0; i < num_user_hostfiles; i++) 807e146993eSDag-Erling Smørgrav load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]); 808e146993eSDag-Erling Smørgrav for (i = 0; i < num_system_hostfiles; i++) 809e146993eSDag-Erling Smørgrav load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]); 810e8aafc91SKris Kennaway } 811e8aafc91SKris Kennaway 812b15c8340SDag-Erling Smørgrav retry: 8134a421b63SDag-Erling Smørgrav /* Reload these as they may have changed on cert->key downgrade */ 814*4f52dfbbSDag-Erling Smørgrav want_cert = sshkey_is_cert(host_key); 815*4f52dfbbSDag-Erling Smørgrav type = sshkey_type(host_key); 816b15c8340SDag-Erling Smørgrav 817e8aafc91SKris Kennaway /* 818b74df5b2SDag-Erling Smørgrav * Check if the host key is present in the user's list of known 819e8aafc91SKris Kennaway * hosts or in the systemwide list. 820e8aafc91SKris Kennaway */ 8214a421b63SDag-Erling Smørgrav host_status = check_key_in_hostkeys(host_hostkeys, host_key, 8224a421b63SDag-Erling Smørgrav &host_found); 8234a421b63SDag-Erling Smørgrav 824e8aafc91SKris Kennaway /* 825e8aafc91SKris Kennaway * Also perform check for the ip address, skip the check if we are 826b15c8340SDag-Erling Smørgrav * localhost, looking for a certificate, or the hostname was an ip 827b15c8340SDag-Erling Smørgrav * address to begin with. 828e8aafc91SKris Kennaway */ 8294a421b63SDag-Erling Smørgrav if (!want_cert && ip_hostkeys != NULL) { 8304a421b63SDag-Erling Smørgrav ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, 8314a421b63SDag-Erling Smørgrav &ip_found); 832e8aafc91SKris Kennaway if (host_status == HOST_CHANGED && 8334a421b63SDag-Erling Smørgrav (ip_status != HOST_CHANGED || 8344a421b63SDag-Erling Smørgrav (ip_found != NULL && 835*4f52dfbbSDag-Erling Smørgrav !sshkey_equal(ip_found->key, host_found->key)))) 836e8aafc91SKris Kennaway host_ip_differ = 1; 837e8aafc91SKris Kennaway } else 838e8aafc91SKris Kennaway ip_status = host_status; 839e8aafc91SKris Kennaway 840e8aafc91SKris Kennaway switch (host_status) { 841e8aafc91SKris Kennaway case HOST_OK: 842e8aafc91SKris Kennaway /* The host is known and the key matches. */ 843b15c8340SDag-Erling Smørgrav debug("Host '%.200s' is known and matches the %s host %s.", 844b15c8340SDag-Erling Smørgrav host, type, want_cert ? "certificate" : "key"); 8454a421b63SDag-Erling Smørgrav debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", 8464a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 847*4f52dfbbSDag-Erling Smørgrav if (want_cert && 848*4f52dfbbSDag-Erling Smørgrav !check_host_cert(options.host_key_alias == NULL ? 849*4f52dfbbSDag-Erling Smørgrav hostname : options.host_key_alias, host_key)) 850b15c8340SDag-Erling Smørgrav goto fail; 851ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 852b15c8340SDag-Erling Smørgrav if (readonly || want_cert) 853cf2b5f3bSDag-Erling Smørgrav logit("%s host key for IP address " 854af12a3e7SDag-Erling Smørgrav "'%.128s' not in list of known hosts.", 855e8aafc91SKris Kennaway type, ip); 856e146993eSDag-Erling Smørgrav else if (!add_host_to_hostfile(user_hostfiles[0], ip, 857aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts)) 858cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the %s host key for IP " 859af12a3e7SDag-Erling Smørgrav "address '%.128s' to the list of known " 860557f75e5SDag-Erling Smørgrav "hosts (%.500s).", type, ip, 861e146993eSDag-Erling Smørgrav user_hostfiles[0]); 862af12a3e7SDag-Erling Smørgrav else 863cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added the %s host " 864af12a3e7SDag-Erling Smørgrav "key for IP address '%.128s' to the list " 865af12a3e7SDag-Erling Smørgrav "of known hosts.", type, ip); 866d4af9e69SDag-Erling Smørgrav } else if (options.visual_host_key) { 867bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(host_key, 868bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT); 869bc5531deSDag-Erling Smørgrav ra = sshkey_fingerprint(host_key, 870bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_RANDOMART); 871bc5531deSDag-Erling Smørgrav if (fp == NULL || ra == NULL) 872bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint fail", __func__); 873acc1a9efSDag-Erling Smørgrav logit("Host key fingerprint is %s\n%s", fp, ra); 874e4a9863fSDag-Erling Smørgrav free(ra); 875e4a9863fSDag-Erling Smørgrav free(fp); 876e8aafc91SKris Kennaway } 877bc5531deSDag-Erling Smørgrav hostkey_trusted = 1; 878e8aafc91SKris Kennaway break; 879e8aafc91SKris Kennaway case HOST_NEW: 880333ee039SDag-Erling Smørgrav if (options.host_key_alias == NULL && port != 0 && 881333ee039SDag-Erling Smørgrav port != SSH_DEFAULT_PORT) { 882333ee039SDag-Erling Smørgrav debug("checking without port identifier"); 883cce7d346SDag-Erling Smørgrav if (check_host_key(hostname, hostaddr, 0, host_key, 884e146993eSDag-Erling Smørgrav ROQUIET, user_hostfiles, num_user_hostfiles, 885e146993eSDag-Erling Smørgrav system_hostfiles, num_system_hostfiles) == 0) { 886333ee039SDag-Erling Smørgrav debug("found matching key w/out port"); 887333ee039SDag-Erling Smørgrav break; 888333ee039SDag-Erling Smørgrav } 889333ee039SDag-Erling Smørgrav } 890b15c8340SDag-Erling Smørgrav if (readonly || want_cert) 891af12a3e7SDag-Erling Smørgrav goto fail; 892e8aafc91SKris Kennaway /* The host is new. */ 893*4f52dfbbSDag-Erling Smørgrav if (options.strict_host_key_checking == 894*4f52dfbbSDag-Erling Smørgrav SSH_STRICT_HOSTKEY_YES) { 895af12a3e7SDag-Erling Smørgrav /* 896af12a3e7SDag-Erling Smørgrav * User has requested strict host key checking. We 897af12a3e7SDag-Erling Smørgrav * will not add the host key automatically. The only 898af12a3e7SDag-Erling Smørgrav * alternative left is to abort. 899af12a3e7SDag-Erling Smørgrav */ 900af12a3e7SDag-Erling Smørgrav error("No %s host key is known for %.200s and you " 901af12a3e7SDag-Erling Smørgrav "have requested strict checking.", type, host); 902af12a3e7SDag-Erling Smørgrav goto fail; 903*4f52dfbbSDag-Erling Smørgrav } else if (options.strict_host_key_checking == 904*4f52dfbbSDag-Erling Smørgrav SSH_STRICT_HOSTKEY_ASK) { 905cf2b5f3bSDag-Erling Smørgrav char msg1[1024], msg2[1024]; 906cf2b5f3bSDag-Erling Smørgrav 9074a421b63SDag-Erling Smørgrav if (show_other_keys(host_hostkeys, host_key)) 908cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), 909cf2b5f3bSDag-Erling Smørgrav "\nbut keys of different type are already" 910cf2b5f3bSDag-Erling Smørgrav " known for this host."); 911cf2b5f3bSDag-Erling Smørgrav else 912cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), "."); 913e8aafc91SKris Kennaway /* The default */ 914bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(host_key, 915bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT); 916bc5531deSDag-Erling Smørgrav ra = sshkey_fingerprint(host_key, 917bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_RANDOMART); 918bc5531deSDag-Erling Smørgrav if (fp == NULL || ra == NULL) 919bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint fail", __func__); 920cf2b5f3bSDag-Erling Smørgrav msg2[0] = '\0'; 921cf2b5f3bSDag-Erling Smørgrav if (options.verify_host_key_dns) { 9221ec0d754SDag-Erling Smørgrav if (matching_host_key_dns) 923cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 924cf2b5f3bSDag-Erling Smørgrav "Matching host key fingerprint" 925cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 926cf2b5f3bSDag-Erling Smørgrav else 927cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 928cf2b5f3bSDag-Erling Smørgrav "No matching host key fingerprint" 929cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 930cf2b5f3bSDag-Erling Smørgrav } 931af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 932af12a3e7SDag-Erling Smørgrav "The authenticity of host '%.200s (%s)' can't be " 933f388f5efSDag-Erling Smørgrav "established%s\n" 934d4af9e69SDag-Erling Smørgrav "%s key fingerprint is %s.%s%s\n%s" 935af12a3e7SDag-Erling Smørgrav "Are you sure you want to continue connecting " 936f388f5efSDag-Erling Smørgrav "(yes/no)? ", 937d4af9e69SDag-Erling Smørgrav host, ip, msg1, type, fp, 938d4af9e69SDag-Erling Smørgrav options.visual_host_key ? "\n" : "", 939d4af9e69SDag-Erling Smørgrav options.visual_host_key ? ra : "", 940d4af9e69SDag-Erling Smørgrav msg2); 941e4a9863fSDag-Erling Smørgrav free(ra); 942e4a9863fSDag-Erling Smørgrav free(fp); 943af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 944af12a3e7SDag-Erling Smørgrav goto fail; 945bc5531deSDag-Erling Smørgrav hostkey_trusted = 1; /* user explicitly confirmed */ 946e8aafc91SKris Kennaway } 947af12a3e7SDag-Erling Smørgrav /* 948*4f52dfbbSDag-Erling Smørgrav * If in "new" or "off" strict mode, add the key automatically 949*4f52dfbbSDag-Erling Smørgrav * to the local known_hosts file. 950af12a3e7SDag-Erling Smørgrav */ 951aa49c926SDag-Erling Smørgrav if (options.check_host_ip && ip_status == HOST_NEW) { 9524a421b63SDag-Erling Smørgrav snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 953aa49c926SDag-Erling Smørgrav hostp = hostline; 954aa49c926SDag-Erling Smørgrav if (options.hash_known_hosts) { 955aa49c926SDag-Erling Smørgrav /* Add hash of host and IP separately */ 956e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], 957e146993eSDag-Erling Smørgrav host, host_key, options.hash_known_hosts) && 958e146993eSDag-Erling Smørgrav add_host_to_hostfile(user_hostfiles[0], ip, 959aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts); 960aa49c926SDag-Erling Smørgrav } else { 961aa49c926SDag-Erling Smørgrav /* Add unhashed "host,ip" */ 962e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], 963aa49c926SDag-Erling Smørgrav hostline, host_key, 964aa49c926SDag-Erling Smørgrav options.hash_known_hosts); 965aa49c926SDag-Erling Smørgrav } 966aa49c926SDag-Erling Smørgrav } else { 967e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], host, 968e146993eSDag-Erling Smørgrav host_key, options.hash_known_hosts); 969aa49c926SDag-Erling Smørgrav hostp = host; 970aa49c926SDag-Erling Smørgrav } 971aa49c926SDag-Erling Smørgrav 972aa49c926SDag-Erling Smørgrav if (!r) 973cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the host to the list of known " 974e146993eSDag-Erling Smørgrav "hosts (%.500s).", user_hostfiles[0]); 975e8aafc91SKris Kennaway else 976cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added '%.200s' (%s) to the " 977af12a3e7SDag-Erling Smørgrav "list of known hosts.", hostp, type); 978e8aafc91SKris Kennaway break; 979b15c8340SDag-Erling Smørgrav case HOST_REVOKED: 980b15c8340SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 981b15c8340SDag-Erling Smørgrav error("@ WARNING: REVOKED HOST KEY DETECTED! @"); 982b15c8340SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 983b15c8340SDag-Erling Smørgrav error("The %s host key for %s is marked as revoked.", type, host); 984b15c8340SDag-Erling Smørgrav error("This could mean that a stolen key is being used to"); 985b15c8340SDag-Erling Smørgrav error("impersonate this host."); 986b15c8340SDag-Erling Smørgrav 987b15c8340SDag-Erling Smørgrav /* 988b15c8340SDag-Erling Smørgrav * If strict host key checking is in use, the user will have 989b15c8340SDag-Erling Smørgrav * to edit the key manually and we can only abort. 990b15c8340SDag-Erling Smørgrav */ 991*4f52dfbbSDag-Erling Smørgrav if (options.strict_host_key_checking != 992*4f52dfbbSDag-Erling Smørgrav SSH_STRICT_HOSTKEY_OFF) { 993b15c8340SDag-Erling Smørgrav error("%s host key for %.200s was revoked and you have " 994b15c8340SDag-Erling Smørgrav "requested strict checking.", type, host); 995b15c8340SDag-Erling Smørgrav goto fail; 996b15c8340SDag-Erling Smørgrav } 997b15c8340SDag-Erling Smørgrav goto continue_unsafe; 998b15c8340SDag-Erling Smørgrav 999e8aafc91SKris Kennaway case HOST_CHANGED: 1000b15c8340SDag-Erling Smørgrav if (want_cert) { 1001b15c8340SDag-Erling Smørgrav /* 1002b15c8340SDag-Erling Smørgrav * This is only a debug() since it is valid to have 1003b15c8340SDag-Erling Smørgrav * CAs with wildcard DNS matches that don't match 1004b15c8340SDag-Erling Smørgrav * all hosts that one might visit. 1005b15c8340SDag-Erling Smørgrav */ 1006b15c8340SDag-Erling Smørgrav debug("Host certificate authority does not " 10074a421b63SDag-Erling Smørgrav "match %s in %s:%lu", CA_MARKER, 10084a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1009b15c8340SDag-Erling Smørgrav goto fail; 1010b15c8340SDag-Erling Smørgrav } 1011333ee039SDag-Erling Smørgrav if (readonly == ROQUIET) 1012333ee039SDag-Erling Smørgrav goto fail; 1013e8aafc91SKris Kennaway if (options.check_host_ip && host_ip_differ) { 101421e764dfSDag-Erling Smørgrav char *key_msg; 1015e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 101621e764dfSDag-Erling Smørgrav key_msg = "is unknown"; 1017e8aafc91SKris Kennaway else if (ip_status == HOST_OK) 101821e764dfSDag-Erling Smørgrav key_msg = "is unchanged"; 1019e8aafc91SKris Kennaway else 102021e764dfSDag-Erling Smørgrav key_msg = "has a different value"; 1021e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1022e8aafc91SKris Kennaway error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 1023e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1024e8aafc91SKris Kennaway error("The %s host key for %s has changed,", type, host); 1025d4af9e69SDag-Erling Smørgrav error("and the key for the corresponding IP address %s", ip); 102621e764dfSDag-Erling Smørgrav error("%s. This could either mean that", key_msg); 1027e8aafc91SKris Kennaway error("DNS SPOOFING is happening or the IP address for the host"); 1028ca3176e7SBrian Feldman error("and its host key have changed at the same time."); 1029ca3176e7SBrian Feldman if (ip_status != HOST_NEW) 10304a421b63SDag-Erling Smørgrav error("Offending key for IP in %s:%lu", 10314a421b63SDag-Erling Smørgrav ip_found->file, ip_found->line); 1032e8aafc91SKris Kennaway } 1033e8aafc91SKris Kennaway /* The host key has changed. */ 10341ec0d754SDag-Erling Smørgrav warn_changed_key(host_key); 1035e8aafc91SKris Kennaway error("Add correct host key in %.100s to get rid of this message.", 1036e146993eSDag-Erling Smørgrav user_hostfiles[0]); 1037*4f52dfbbSDag-Erling Smørgrav error("Offending %s key in %s:%lu", 1038*4f52dfbbSDag-Erling Smørgrav sshkey_type(host_found->key), 10394a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1040e8aafc91SKris Kennaway 1041e8aafc91SKris Kennaway /* 1042e8aafc91SKris Kennaway * If strict host key checking is in use, the user will have 1043e8aafc91SKris Kennaway * to edit the key manually and we can only abort. 1044e8aafc91SKris Kennaway */ 1045*4f52dfbbSDag-Erling Smørgrav if (options.strict_host_key_checking != 1046*4f52dfbbSDag-Erling Smørgrav SSH_STRICT_HOSTKEY_OFF) { 1047af12a3e7SDag-Erling Smørgrav error("%s host key for %.200s has changed and you have " 1048af12a3e7SDag-Erling Smørgrav "requested strict checking.", type, host); 1049af12a3e7SDag-Erling Smørgrav goto fail; 1050af12a3e7SDag-Erling Smørgrav } 1051e8aafc91SKris Kennaway 1052b15c8340SDag-Erling Smørgrav continue_unsafe: 1053e8aafc91SKris Kennaway /* 1054e8aafc91SKris Kennaway * If strict host key checking has not been requested, allow 1055cf2b5f3bSDag-Erling Smørgrav * the connection but without MITM-able authentication or 1056333ee039SDag-Erling Smørgrav * forwarding. 1057e8aafc91SKris Kennaway */ 1058e8aafc91SKris Kennaway if (options.password_authentication) { 1059af12a3e7SDag-Erling Smørgrav error("Password authentication is disabled to avoid " 1060af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1061e8aafc91SKris Kennaway options.password_authentication = 0; 1062d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1063e8aafc91SKris Kennaway } 1064cf2b5f3bSDag-Erling Smørgrav if (options.kbd_interactive_authentication) { 1065cf2b5f3bSDag-Erling Smørgrav error("Keyboard-interactive authentication is disabled" 1066cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 1067cf2b5f3bSDag-Erling Smørgrav options.kbd_interactive_authentication = 0; 1068cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 1069d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1070cf2b5f3bSDag-Erling Smørgrav } 1071cf2b5f3bSDag-Erling Smørgrav if (options.challenge_response_authentication) { 1072cf2b5f3bSDag-Erling Smørgrav error("Challenge/response authentication is disabled" 1073cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 1074cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 1075d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1076cf2b5f3bSDag-Erling Smørgrav } 1077e8aafc91SKris Kennaway if (options.forward_agent) { 1078af12a3e7SDag-Erling Smørgrav error("Agent forwarding is disabled to avoid " 1079af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1080e8aafc91SKris Kennaway options.forward_agent = 0; 1081d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1082e8aafc91SKris Kennaway } 1083ca3176e7SBrian Feldman if (options.forward_x11) { 1084af12a3e7SDag-Erling Smørgrav error("X11 forwarding is disabled to avoid " 1085af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1086ca3176e7SBrian Feldman options.forward_x11 = 0; 1087d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1088ca3176e7SBrian Feldman } 1089af12a3e7SDag-Erling Smørgrav if (options.num_local_forwards > 0 || 1090af12a3e7SDag-Erling Smørgrav options.num_remote_forwards > 0) { 1091af12a3e7SDag-Erling Smørgrav error("Port forwarding is disabled to avoid " 1092af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1093af12a3e7SDag-Erling Smørgrav options.num_local_forwards = 1094af12a3e7SDag-Erling Smørgrav options.num_remote_forwards = 0; 1095d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1096ca3176e7SBrian Feldman } 1097333ee039SDag-Erling Smørgrav if (options.tun_open != SSH_TUNMODE_NO) { 1098333ee039SDag-Erling Smørgrav error("Tunnel forwarding is disabled to avoid " 1099333ee039SDag-Erling Smørgrav "man-in-the-middle attacks."); 1100333ee039SDag-Erling Smørgrav options.tun_open = SSH_TUNMODE_NO; 1101d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1102333ee039SDag-Erling Smørgrav } 1103d4af9e69SDag-Erling Smørgrav if (options.exit_on_forward_failure && cancelled_forwarding) 1104d4af9e69SDag-Erling Smørgrav fatal("Error: forwarding disabled due to host key " 1105d4af9e69SDag-Erling Smørgrav "check failure"); 1106d4af9e69SDag-Erling Smørgrav 1107e8aafc91SKris Kennaway /* 1108e8aafc91SKris Kennaway * XXX Should permit the user to change to use the new id. 1109e8aafc91SKris Kennaway * This could be done by converting the host key to an 1110e8aafc91SKris Kennaway * identifying sentence, tell that the host identifies itself 1111b15c8340SDag-Erling Smørgrav * by that sentence, and ask the user if he/she wishes to 1112e8aafc91SKris Kennaway * accept the authentication. 1113e8aafc91SKris Kennaway */ 1114e8aafc91SKris Kennaway break; 1115f388f5efSDag-Erling Smørgrav case HOST_FOUND: 1116f388f5efSDag-Erling Smørgrav fatal("internal error"); 1117f388f5efSDag-Erling Smørgrav break; 1118e8aafc91SKris Kennaway } 1119ca3176e7SBrian Feldman 1120ca3176e7SBrian Feldman if (options.check_host_ip && host_status != HOST_CHANGED && 1121ca3176e7SBrian Feldman ip_status == HOST_CHANGED) { 1122af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 1123af12a3e7SDag-Erling Smørgrav "Warning: the %s host key for '%.200s' " 1124af12a3e7SDag-Erling Smørgrav "differs from the key for the IP address '%.128s'" 11254a421b63SDag-Erling Smørgrav "\nOffending key for IP in %s:%lu", 11264a421b63SDag-Erling Smørgrav type, host, ip, ip_found->file, ip_found->line); 1127af12a3e7SDag-Erling Smørgrav if (host_status == HOST_OK) { 1128af12a3e7SDag-Erling Smørgrav len = strlen(msg); 1129af12a3e7SDag-Erling Smørgrav snprintf(msg + len, sizeof(msg) - len, 11304a421b63SDag-Erling Smørgrav "\nMatching host key in %s:%lu", 11314a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1132af12a3e7SDag-Erling Smørgrav } 1133*4f52dfbbSDag-Erling Smørgrav if (options.strict_host_key_checking == 1134*4f52dfbbSDag-Erling Smørgrav SSH_STRICT_HOSTKEY_ASK) { 1135af12a3e7SDag-Erling Smørgrav strlcat(msg, "\nAre you sure you want " 1136af12a3e7SDag-Erling Smørgrav "to continue connecting (yes/no)? ", sizeof(msg)); 1137af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 1138af12a3e7SDag-Erling Smørgrav goto fail; 1139*4f52dfbbSDag-Erling Smørgrav } else if (options.strict_host_key_checking != 1140*4f52dfbbSDag-Erling Smørgrav SSH_STRICT_HOSTKEY_OFF) { 1141*4f52dfbbSDag-Erling Smørgrav logit("%s", msg); 1142*4f52dfbbSDag-Erling Smørgrav error("Exiting, you have requested strict checking."); 1143*4f52dfbbSDag-Erling Smørgrav goto fail; 1144af12a3e7SDag-Erling Smørgrav } else { 1145cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 1146ca3176e7SBrian Feldman } 1147ca3176e7SBrian Feldman } 1148ca3176e7SBrian Feldman 1149bc5531deSDag-Erling Smørgrav if (!hostkey_trusted && options.update_hostkeys) { 1150bc5531deSDag-Erling Smørgrav debug("%s: hostkey not known or explicitly trusted: " 1151bc5531deSDag-Erling Smørgrav "disabling UpdateHostkeys", __func__); 1152bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 1153bc5531deSDag-Erling Smørgrav } 1154bc5531deSDag-Erling Smørgrav 1155e4a9863fSDag-Erling Smørgrav free(ip); 1156e4a9863fSDag-Erling Smørgrav free(host); 11574a421b63SDag-Erling Smørgrav if (host_hostkeys != NULL) 11584a421b63SDag-Erling Smørgrav free_hostkeys(host_hostkeys); 11594a421b63SDag-Erling Smørgrav if (ip_hostkeys != NULL) 11604a421b63SDag-Erling Smørgrav free_hostkeys(ip_hostkeys); 1161af12a3e7SDag-Erling Smørgrav return 0; 1162af12a3e7SDag-Erling Smørgrav 1163af12a3e7SDag-Erling Smørgrav fail: 1164b15c8340SDag-Erling Smørgrav if (want_cert && host_status != HOST_REVOKED) { 1165b15c8340SDag-Erling Smørgrav /* 1166b15c8340SDag-Erling Smørgrav * No matching certificate. Downgrade cert to raw key and 1167b15c8340SDag-Erling Smørgrav * search normally. 1168b15c8340SDag-Erling Smørgrav */ 1169b15c8340SDag-Erling Smørgrav debug("No matching CA found. Retry with plain key"); 1170*4f52dfbbSDag-Erling Smørgrav if ((r = sshkey_from_private(host_key, &raw_key)) != 0) 1171*4f52dfbbSDag-Erling Smørgrav fatal("%s: sshkey_from_private: %s", 1172*4f52dfbbSDag-Erling Smørgrav __func__, ssh_err(r)); 1173*4f52dfbbSDag-Erling Smørgrav if ((r = sshkey_drop_cert(raw_key)) != 0) 1174*4f52dfbbSDag-Erling Smørgrav fatal("Couldn't drop certificate: %s", ssh_err(r)); 1175b15c8340SDag-Erling Smørgrav host_key = raw_key; 1176b15c8340SDag-Erling Smørgrav goto retry; 1177b15c8340SDag-Erling Smørgrav } 1178b15c8340SDag-Erling Smørgrav if (raw_key != NULL) 1179*4f52dfbbSDag-Erling Smørgrav sshkey_free(raw_key); 1180e4a9863fSDag-Erling Smørgrav free(ip); 1181e4a9863fSDag-Erling Smørgrav free(host); 11824a421b63SDag-Erling Smørgrav if (host_hostkeys != NULL) 11834a421b63SDag-Erling Smørgrav free_hostkeys(host_hostkeys); 11844a421b63SDag-Erling Smørgrav if (ip_hostkeys != NULL) 11854a421b63SDag-Erling Smørgrav free_hostkeys(ip_hostkeys); 1186af12a3e7SDag-Erling Smørgrav return -1; 1187e8aafc91SKris Kennaway } 1188511b41d2SMark Murray 1189cf2b5f3bSDag-Erling Smørgrav /* returns 0 if key verifies or -1 if key does NOT verify */ 1190fe5fd017SMark Murray int 1191*4f52dfbbSDag-Erling Smørgrav verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key) 1192fe5fd017SMark Murray { 1193acc1a9efSDag-Erling Smørgrav u_int i; 1194a0ee8cc6SDag-Erling Smørgrav int r = -1, flags = 0; 1195acc1a9efSDag-Erling Smørgrav char valid[64], *fp = NULL, *cafp = NULL; 1196bc5531deSDag-Erling Smørgrav struct sshkey *plain = NULL; 11974a421b63SDag-Erling Smørgrav 1198bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(host_key, 1199bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1200bc5531deSDag-Erling Smørgrav error("%s: fingerprint host key: %s", __func__, ssh_err(r)); 1201bc5531deSDag-Erling Smørgrav r = -1; 1202bc5531deSDag-Erling Smørgrav goto out; 1203bc5531deSDag-Erling Smørgrav } 1204fe5fd017SMark Murray 1205acc1a9efSDag-Erling Smørgrav if (sshkey_is_cert(host_key)) { 1206acc1a9efSDag-Erling Smørgrav if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, 1207acc1a9efSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1208acc1a9efSDag-Erling Smørgrav error("%s: fingerprint CA key: %s", 1209acc1a9efSDag-Erling Smørgrav __func__, ssh_err(r)); 1210acc1a9efSDag-Erling Smørgrav r = -1; 1211acc1a9efSDag-Erling Smørgrav goto out; 1212acc1a9efSDag-Erling Smørgrav } 1213acc1a9efSDag-Erling Smørgrav sshkey_format_cert_validity(host_key->cert, 1214acc1a9efSDag-Erling Smørgrav valid, sizeof(valid)); 1215acc1a9efSDag-Erling Smørgrav debug("Server host certificate: %s %s, serial %llu " 1216acc1a9efSDag-Erling Smørgrav "ID \"%s\" CA %s %s valid %s", 1217acc1a9efSDag-Erling Smørgrav sshkey_ssh_name(host_key), fp, 1218acc1a9efSDag-Erling Smørgrav (unsigned long long)host_key->cert->serial, 1219acc1a9efSDag-Erling Smørgrav host_key->cert->key_id, 1220acc1a9efSDag-Erling Smørgrav sshkey_ssh_name(host_key->cert->signature_key), cafp, 1221acc1a9efSDag-Erling Smørgrav valid); 1222acc1a9efSDag-Erling Smørgrav for (i = 0; i < host_key->cert->nprincipals; i++) { 1223acc1a9efSDag-Erling Smørgrav debug2("Server host certificate hostname: %s", 1224acc1a9efSDag-Erling Smørgrav host_key->cert->principals[i]); 1225acc1a9efSDag-Erling Smørgrav } 1226acc1a9efSDag-Erling Smørgrav } else { 1227*4f52dfbbSDag-Erling Smørgrav debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp); 1228acc1a9efSDag-Erling Smørgrav } 1229bc5531deSDag-Erling Smørgrav 1230bc5531deSDag-Erling Smørgrav if (sshkey_equal(previous_host_key, host_key)) { 1231bc5531deSDag-Erling Smørgrav debug2("%s: server host key %s %s matches cached key", 1232bc5531deSDag-Erling Smørgrav __func__, sshkey_type(host_key), fp); 1233bc5531deSDag-Erling Smørgrav r = 0; 1234bc5531deSDag-Erling Smørgrav goto out; 1235bc5531deSDag-Erling Smørgrav } 1236bc5531deSDag-Erling Smørgrav 1237bc5531deSDag-Erling Smørgrav /* Check in RevokedHostKeys file if specified */ 1238bc5531deSDag-Erling Smørgrav if (options.revoked_host_keys != NULL) { 1239bc5531deSDag-Erling Smørgrav r = sshkey_check_revoked(host_key, options.revoked_host_keys); 1240bc5531deSDag-Erling Smørgrav switch (r) { 1241bc5531deSDag-Erling Smørgrav case 0: 1242bc5531deSDag-Erling Smørgrav break; /* not revoked */ 1243bc5531deSDag-Erling Smørgrav case SSH_ERR_KEY_REVOKED: 1244bc5531deSDag-Erling Smørgrav error("Host key %s %s revoked by file %s", 1245bc5531deSDag-Erling Smørgrav sshkey_type(host_key), fp, 1246bc5531deSDag-Erling Smørgrav options.revoked_host_keys); 1247bc5531deSDag-Erling Smørgrav r = -1; 1248bc5531deSDag-Erling Smørgrav goto out; 1249bc5531deSDag-Erling Smørgrav default: 1250bc5531deSDag-Erling Smørgrav error("Error checking host key %s %s in " 1251bc5531deSDag-Erling Smørgrav "revoked keys file %s: %s", sshkey_type(host_key), 1252bc5531deSDag-Erling Smørgrav fp, options.revoked_host_keys, ssh_err(r)); 1253bc5531deSDag-Erling Smørgrav r = -1; 1254bc5531deSDag-Erling Smørgrav goto out; 1255bc5531deSDag-Erling Smørgrav } 1256a0ee8cc6SDag-Erling Smørgrav } 1257a0ee8cc6SDag-Erling Smørgrav 12583a0b9b77SXin LI if (options.verify_host_key_dns) { 12593a0b9b77SXin LI /* 12603a0b9b77SXin LI * XXX certs are not yet supported for DNS, so downgrade 12613a0b9b77SXin LI * them and try the plain key. 12623a0b9b77SXin LI */ 1263bc5531deSDag-Erling Smørgrav if ((r = sshkey_from_private(host_key, &plain)) != 0) 1264bc5531deSDag-Erling Smørgrav goto out; 1265bc5531deSDag-Erling Smørgrav if (sshkey_is_cert(plain)) 1266bc5531deSDag-Erling Smørgrav sshkey_drop_cert(plain); 12673a0b9b77SXin LI if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) { 12681ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_FOUND) { 12691ec0d754SDag-Erling Smørgrav if (options.verify_host_key_dns == 1 && 12701ec0d754SDag-Erling Smørgrav flags & DNS_VERIFY_MATCH && 12713a0b9b77SXin LI flags & DNS_VERIFY_SECURE) { 1272a0ee8cc6SDag-Erling Smørgrav r = 0; 1273bc5531deSDag-Erling Smørgrav goto out; 12743a0b9b77SXin LI } 12751ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_MATCH) { 12761ec0d754SDag-Erling Smørgrav matching_host_key_dns = 1; 12771ec0d754SDag-Erling Smørgrav } else { 12783a0b9b77SXin LI warn_changed_key(plain); 12793a0b9b77SXin LI error("Update the SSHFP RR in DNS " 12803a0b9b77SXin LI "with the new host key to get rid " 12813a0b9b77SXin LI "of this message."); 1282cf2b5f3bSDag-Erling Smørgrav } 1283cf2b5f3bSDag-Erling Smørgrav } 12841ec0d754SDag-Erling Smørgrav } 12853a0b9b77SXin LI } 1286a0ee8cc6SDag-Erling Smørgrav r = check_host_key(host, hostaddr, options.port, host_key, RDRW, 1287e146993eSDag-Erling Smørgrav options.user_hostfiles, options.num_user_hostfiles, 1288e146993eSDag-Erling Smørgrav options.system_hostfiles, options.num_system_hostfiles); 1289a0ee8cc6SDag-Erling Smørgrav 1290bc5531deSDag-Erling Smørgrav out: 1291bc5531deSDag-Erling Smørgrav sshkey_free(plain); 1292bc5531deSDag-Erling Smørgrav free(fp); 1293acc1a9efSDag-Erling Smørgrav free(cafp); 1294a0ee8cc6SDag-Erling Smørgrav if (r == 0 && host_key != NULL) { 1295*4f52dfbbSDag-Erling Smørgrav sshkey_free(previous_host_key); 1296*4f52dfbbSDag-Erling Smørgrav r = sshkey_from_private(host_key, &previous_host_key); 1297a0ee8cc6SDag-Erling Smørgrav } 1298a0ee8cc6SDag-Erling Smørgrav 1299a0ee8cc6SDag-Erling Smørgrav return r; 1300fe5fd017SMark Murray } 1301fe5fd017SMark Murray 1302511b41d2SMark Murray /* 1303511b41d2SMark Murray * Starts a dialog with the server, and authenticates the current user on the 1304511b41d2SMark Murray * server. This does not need any extra privileges. The basic connection 1305511b41d2SMark Murray * to the server must already have been established before this is called. 1306511b41d2SMark Murray * If login fails, this function prints an error and never returns. 1307511b41d2SMark Murray * This function does not require super-user privileges. 1308511b41d2SMark Murray */ 1309511b41d2SMark Murray void 131080628bacSDag-Erling Smørgrav ssh_login(Sensitive *sensitive, const char *orighost, 13114a421b63SDag-Erling Smørgrav struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) 1312511b41d2SMark Murray { 1313f7167e0eSDag-Erling Smørgrav char *host; 1314e8aafc91SKris Kennaway char *server_user, *local_user; 1315e8aafc91SKris Kennaway 1316e8aafc91SKris Kennaway local_user = xstrdup(pw->pw_name); 1317e8aafc91SKris Kennaway server_user = options.user ? options.user : local_user; 1318511b41d2SMark Murray 1319511b41d2SMark Murray /* Convert the user-supplied hostname into all lowercase. */ 1320511b41d2SMark Murray host = xstrdup(orighost); 1321f7167e0eSDag-Erling Smørgrav lowercase(host); 1322511b41d2SMark Murray 1323511b41d2SMark Murray /* Exchange protocol version identification strings with the server. */ 1324d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(timeout_ms); 1325511b41d2SMark Murray 1326511b41d2SMark Murray /* Put the connection into non-blocking mode. */ 1327511b41d2SMark Murray packet_set_nonblocking(); 1328511b41d2SMark Murray 1329511b41d2SMark Murray /* key exchange */ 1330511b41d2SMark Murray /* authenticate user */ 1331557f75e5SDag-Erling Smørgrav debug("Authenticating to %s:%d as '%s'", host, port, server_user); 13324a421b63SDag-Erling Smørgrav ssh_kex2(host, hostaddr, port); 133380628bacSDag-Erling Smørgrav ssh_userauth2(local_user, server_user, host, sensitive); 1334e4a9863fSDag-Erling Smørgrav free(local_user); 1335511b41d2SMark Murray } 1336e0fbb1d2SBrian Feldman 1337e0fbb1d2SBrian Feldman void 1338e0fbb1d2SBrian Feldman ssh_put_password(char *password) 1339e0fbb1d2SBrian Feldman { 1340e0fbb1d2SBrian Feldman int size; 1341e0fbb1d2SBrian Feldman char *padded; 1342e0fbb1d2SBrian Feldman 1343ca3176e7SBrian Feldman if (datafellows & SSH_BUG_PASSWORDPAD) { 1344af12a3e7SDag-Erling Smørgrav packet_put_cstring(password); 1345ca3176e7SBrian Feldman return; 1346ca3176e7SBrian Feldman } 1347ca86bcf2SDag-Erling Smørgrav size = ROUNDUP(strlen(password) + 1, 32); 1348333ee039SDag-Erling Smørgrav padded = xcalloc(1, size); 1349e0fbb1d2SBrian Feldman strlcpy(padded, password, size); 1350e0fbb1d2SBrian Feldman packet_put_string(padded, size); 1351b83788ffSDag-Erling Smørgrav explicit_bzero(padded, size); 1352e4a9863fSDag-Erling Smørgrav free(padded); 1353e0fbb1d2SBrian Feldman } 1354f388f5efSDag-Erling Smørgrav 1355f388f5efSDag-Erling Smørgrav /* print all known host keys for a given host, but skip keys of given type */ 1356f388f5efSDag-Erling Smørgrav static int 1357*4f52dfbbSDag-Erling Smørgrav show_other_keys(struct hostkeys *hostkeys, struct sshkey *key) 1358f388f5efSDag-Erling Smørgrav { 1359f7167e0eSDag-Erling Smørgrav int type[] = { 1360f7167e0eSDag-Erling Smørgrav KEY_RSA, 1361f7167e0eSDag-Erling Smørgrav KEY_DSA, 1362f7167e0eSDag-Erling Smørgrav KEY_ECDSA, 1363f7167e0eSDag-Erling Smørgrav KEY_ED25519, 1364f7167e0eSDag-Erling Smørgrav -1 1365f7167e0eSDag-Erling Smørgrav }; 13664a421b63SDag-Erling Smørgrav int i, ret = 0; 13674a421b63SDag-Erling Smørgrav char *fp, *ra; 13684a421b63SDag-Erling Smørgrav const struct hostkey_entry *found; 1369f388f5efSDag-Erling Smørgrav 1370f388f5efSDag-Erling Smørgrav for (i = 0; type[i] != -1; i++) { 1371f388f5efSDag-Erling Smørgrav if (type[i] == key->type) 1372f388f5efSDag-Erling Smørgrav continue; 13734a421b63SDag-Erling Smørgrav if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) 1374f388f5efSDag-Erling Smørgrav continue; 1375bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(found->key, 1376bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT); 1377bc5531deSDag-Erling Smørgrav ra = sshkey_fingerprint(found->key, 1378bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_RANDOMART); 1379bc5531deSDag-Erling Smørgrav if (fp == NULL || ra == NULL) 1380bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint fail", __func__); 13814a421b63SDag-Erling Smørgrav logit("WARNING: %s key found for host %s\n" 13824a421b63SDag-Erling Smørgrav "in %s:%lu\n" 13834a421b63SDag-Erling Smørgrav "%s key fingerprint %s.", 13844a421b63SDag-Erling Smørgrav key_type(found->key), 13854a421b63SDag-Erling Smørgrav found->host, found->file, found->line, 13864a421b63SDag-Erling Smørgrav key_type(found->key), fp); 13874a421b63SDag-Erling Smørgrav if (options.visual_host_key) 13884a421b63SDag-Erling Smørgrav logit("%s", ra); 1389e4a9863fSDag-Erling Smørgrav free(ra); 1390e4a9863fSDag-Erling Smørgrav free(fp); 13914a421b63SDag-Erling Smørgrav ret = 1; 1392f388f5efSDag-Erling Smørgrav } 13934a421b63SDag-Erling Smørgrav return ret; 1394f388f5efSDag-Erling Smørgrav } 13951ec0d754SDag-Erling Smørgrav 13961ec0d754SDag-Erling Smørgrav static void 1397*4f52dfbbSDag-Erling Smørgrav warn_changed_key(struct sshkey *host_key) 13981ec0d754SDag-Erling Smørgrav { 13991ec0d754SDag-Erling Smørgrav char *fp; 14001ec0d754SDag-Erling Smørgrav 1401bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(host_key, options.fingerprint_hash, 1402bc5531deSDag-Erling Smørgrav SSH_FP_DEFAULT); 1403bc5531deSDag-Erling Smørgrav if (fp == NULL) 1404bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint fail", __func__); 14051ec0d754SDag-Erling Smørgrav 14061ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 14071ec0d754SDag-Erling Smørgrav error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 14081ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 14091ec0d754SDag-Erling Smørgrav error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 14101ec0d754SDag-Erling Smørgrav error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 14114a421b63SDag-Erling Smørgrav error("It is also possible that a host key has just been changed."); 14121ec0d754SDag-Erling Smørgrav error("The fingerprint for the %s key sent by the remote host is\n%s.", 14134a421b63SDag-Erling Smørgrav key_type(host_key), fp); 14141ec0d754SDag-Erling Smørgrav error("Please contact your system administrator."); 14151ec0d754SDag-Erling Smørgrav 1416e4a9863fSDag-Erling Smørgrav free(fp); 14171ec0d754SDag-Erling Smørgrav } 1418b74df5b2SDag-Erling Smørgrav 1419b74df5b2SDag-Erling Smørgrav /* 1420b74df5b2SDag-Erling Smørgrav * Execute a local command 1421b74df5b2SDag-Erling Smørgrav */ 1422b74df5b2SDag-Erling Smørgrav int 1423b74df5b2SDag-Erling Smørgrav ssh_local_cmd(const char *args) 1424b74df5b2SDag-Erling Smørgrav { 1425b74df5b2SDag-Erling Smørgrav char *shell; 1426b74df5b2SDag-Erling Smørgrav pid_t pid; 1427b74df5b2SDag-Erling Smørgrav int status; 14284a421b63SDag-Erling Smørgrav void (*osighand)(int); 1429b74df5b2SDag-Erling Smørgrav 1430b74df5b2SDag-Erling Smørgrav if (!options.permit_local_command || 1431b74df5b2SDag-Erling Smørgrav args == NULL || !*args) 1432b74df5b2SDag-Erling Smørgrav return (1); 1433b74df5b2SDag-Erling Smørgrav 14344a421b63SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 1435b74df5b2SDag-Erling Smørgrav shell = _PATH_BSHELL; 1436b74df5b2SDag-Erling Smørgrav 14374a421b63SDag-Erling Smørgrav osighand = signal(SIGCHLD, SIG_DFL); 1438b74df5b2SDag-Erling Smørgrav pid = fork(); 1439b74df5b2SDag-Erling Smørgrav if (pid == 0) { 14404a421b63SDag-Erling Smørgrav signal(SIGPIPE, SIG_DFL); 1441b74df5b2SDag-Erling Smørgrav debug3("Executing %s -c \"%s\"", shell, args); 1442b74df5b2SDag-Erling Smørgrav execl(shell, shell, "-c", args, (char *)NULL); 1443b74df5b2SDag-Erling Smørgrav error("Couldn't execute %s -c \"%s\": %s", 1444b74df5b2SDag-Erling Smørgrav shell, args, strerror(errno)); 1445b74df5b2SDag-Erling Smørgrav _exit(1); 1446b74df5b2SDag-Erling Smørgrav } else if (pid == -1) 1447b74df5b2SDag-Erling Smørgrav fatal("fork failed: %.100s", strerror(errno)); 1448b74df5b2SDag-Erling Smørgrav while (waitpid(pid, &status, 0) == -1) 1449b74df5b2SDag-Erling Smørgrav if (errno != EINTR) 1450b74df5b2SDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 14514a421b63SDag-Erling Smørgrav signal(SIGCHLD, osighand); 1452b74df5b2SDag-Erling Smørgrav 1453b74df5b2SDag-Erling Smørgrav if (!WIFEXITED(status)) 1454b74df5b2SDag-Erling Smørgrav return (1); 1455b74df5b2SDag-Erling Smørgrav 1456b74df5b2SDag-Erling Smørgrav return (WEXITSTATUS(status)); 1457b74df5b2SDag-Erling Smørgrav } 1458acc1a9efSDag-Erling Smørgrav 1459acc1a9efSDag-Erling Smørgrav void 1460*4f52dfbbSDag-Erling Smørgrav maybe_add_key_to_agent(char *authfile, struct sshkey *private, char *comment, 1461acc1a9efSDag-Erling Smørgrav char *passphrase) 1462acc1a9efSDag-Erling Smørgrav { 1463acc1a9efSDag-Erling Smørgrav int auth_sock = -1, r; 1464acc1a9efSDag-Erling Smørgrav 1465acc1a9efSDag-Erling Smørgrav if (options.add_keys_to_agent == 0) 1466acc1a9efSDag-Erling Smørgrav return; 1467acc1a9efSDag-Erling Smørgrav 1468acc1a9efSDag-Erling Smørgrav if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { 1469acc1a9efSDag-Erling Smørgrav debug3("no authentication agent, not adding key"); 1470acc1a9efSDag-Erling Smørgrav return; 1471acc1a9efSDag-Erling Smørgrav } 1472acc1a9efSDag-Erling Smørgrav 1473acc1a9efSDag-Erling Smørgrav if (options.add_keys_to_agent == 2 && 1474acc1a9efSDag-Erling Smørgrav !ask_permission("Add key %s (%s) to agent?", authfile, comment)) { 1475acc1a9efSDag-Erling Smørgrav debug3("user denied adding this key"); 1476d93a896eSDag-Erling Smørgrav close(auth_sock); 1477acc1a9efSDag-Erling Smørgrav return; 1478acc1a9efSDag-Erling Smørgrav } 1479acc1a9efSDag-Erling Smørgrav 1480acc1a9efSDag-Erling Smørgrav if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, 1481acc1a9efSDag-Erling Smørgrav (options.add_keys_to_agent == 3))) == 0) 1482acc1a9efSDag-Erling Smørgrav debug("identity added to agent: %s", authfile); 1483acc1a9efSDag-Erling Smørgrav else 1484acc1a9efSDag-Erling Smørgrav debug("could not add identity to agent: %s (%d)", authfile, r); 1485d93a896eSDag-Erling Smørgrav close(auth_sock); 1486acc1a9efSDag-Erling Smørgrav } 1487