1*acc1a9efSDag-Erling Smørgrav /* $OpenBSD: sshconnect.c,v 1.271 2016/01/14 22:56:56 markus 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 19bc5531deSDag-Erling Smørgrav #include <sys/param.h> /* roundup */ 20333ee039SDag-Erling Smørgrav #include <sys/types.h> 21333ee039SDag-Erling Smørgrav #include <sys/wait.h> 22333ee039SDag-Erling Smørgrav #include <sys/stat.h> 23333ee039SDag-Erling Smørgrav #include <sys/socket.h> 24333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 25333ee039SDag-Erling Smørgrav # include <sys/time.h> 26333ee039SDag-Erling Smørgrav #endif 27e8aafc91SKris Kennaway 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> 404a421b63SDag-Erling Smørgrav #include <signal.h> 41333ee039SDag-Erling Smørgrav #include <stdarg.h> 42333ee039SDag-Erling Smørgrav #include <stdio.h> 43333ee039SDag-Erling Smørgrav #include <stdlib.h> 44333ee039SDag-Erling Smørgrav #include <string.h> 45333ee039SDag-Erling Smørgrav #include <unistd.h> 46333ee039SDag-Erling Smørgrav 47511b41d2SMark Murray #include "xmalloc.h" 48333ee039SDag-Erling Smørgrav #include "key.h" 49333ee039SDag-Erling Smørgrav #include "hostfile.h" 50333ee039SDag-Erling Smørgrav #include "ssh.h" 51511b41d2SMark Murray #include "rsa.h" 52e8aafc91SKris Kennaway #include "buffer.h" 53511b41d2SMark Murray #include "packet.h" 54511b41d2SMark Murray #include "uidswap.h" 55511b41d2SMark Murray #include "compat.h" 563c6ae118SKris Kennaway #include "key.h" 57e8aafc91SKris Kennaway #include "sshconnect.h" 583c6ae118SKris Kennaway #include "hostfile.h" 59ca3176e7SBrian Feldman #include "log.h" 60a0ee8cc6SDag-Erling Smørgrav #include "misc.h" 61ca3176e7SBrian Feldman #include "readconf.h" 62ca3176e7SBrian Feldman #include "atomicio.h" 63cf2b5f3bSDag-Erling Smørgrav #include "dns.h" 64f7167e0eSDag-Erling Smørgrav #include "monitor_fdpass.h" 65b15c8340SDag-Erling Smørgrav #include "ssh2.h" 66333ee039SDag-Erling Smørgrav #include "version.h" 67bc5531deSDag-Erling Smørgrav #include "authfile.h" 68bc5531deSDag-Erling Smørgrav #include "ssherr.h" 69*acc1a9efSDag-Erling Smørgrav #include "authfd.h" 70cf2b5f3bSDag-Erling Smørgrav 71e8aafc91SKris Kennaway char *client_version_string = NULL; 72e8aafc91SKris Kennaway char *server_version_string = NULL; 73a0ee8cc6SDag-Erling Smørgrav Key *previous_host_key = NULL; 74511b41d2SMark Murray 75b74df5b2SDag-Erling Smørgrav static int matching_host_key_dns = 0; 76cf2b5f3bSDag-Erling Smørgrav 774a421b63SDag-Erling Smørgrav static pid_t proxy_command_pid = 0; 784a421b63SDag-Erling Smørgrav 7980628bacSDag-Erling Smørgrav /* import */ 80511b41d2SMark Murray extern Options options; 81511b41d2SMark Murray extern char *__progname; 8280628bacSDag-Erling Smørgrav extern uid_t original_real_uid; 8380628bacSDag-Erling Smørgrav extern uid_t original_effective_uid; 84511b41d2SMark Murray 854a421b63SDag-Erling Smørgrav static int show_other_keys(struct hostkeys *, Key *); 861ec0d754SDag-Erling Smørgrav static void warn_changed_key(Key *); 87ca3176e7SBrian Feldman 88f7167e0eSDag-Erling Smørgrav /* Expand a proxy command */ 89f7167e0eSDag-Erling Smørgrav static char * 90f7167e0eSDag-Erling Smørgrav expand_proxy_command(const char *proxy_command, const char *user, 91f7167e0eSDag-Erling Smørgrav const char *host, int port) 92f7167e0eSDag-Erling Smørgrav { 93f7167e0eSDag-Erling Smørgrav char *tmp, *ret, strport[NI_MAXSERV]; 94f7167e0eSDag-Erling Smørgrav 95f7167e0eSDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", port); 96f7167e0eSDag-Erling Smørgrav xasprintf(&tmp, "exec %s", proxy_command); 97f7167e0eSDag-Erling Smørgrav ret = percent_expand(tmp, "h", host, "p", strport, 98f7167e0eSDag-Erling Smørgrav "r", options.user, (char *)NULL); 99f7167e0eSDag-Erling Smørgrav free(tmp); 100f7167e0eSDag-Erling Smørgrav return ret; 101f7167e0eSDag-Erling Smørgrav } 102f7167e0eSDag-Erling Smørgrav 103f7167e0eSDag-Erling Smørgrav /* 104f7167e0eSDag-Erling Smørgrav * Connect to the given ssh server using a proxy command that passes a 105f7167e0eSDag-Erling Smørgrav * a connected fd back to us. 106f7167e0eSDag-Erling Smørgrav */ 107f7167e0eSDag-Erling Smørgrav static int 108f7167e0eSDag-Erling Smørgrav ssh_proxy_fdpass_connect(const char *host, u_short port, 109f7167e0eSDag-Erling Smørgrav const char *proxy_command) 110f7167e0eSDag-Erling Smørgrav { 111f7167e0eSDag-Erling Smørgrav char *command_string; 112f7167e0eSDag-Erling Smørgrav int sp[2], sock; 113f7167e0eSDag-Erling Smørgrav pid_t pid; 114f7167e0eSDag-Erling Smørgrav char *shell; 115f7167e0eSDag-Erling Smørgrav 116f7167e0eSDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL) 117f7167e0eSDag-Erling Smørgrav shell = _PATH_BSHELL; 118f7167e0eSDag-Erling Smørgrav 119f7167e0eSDag-Erling Smørgrav if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) 120f7167e0eSDag-Erling Smørgrav fatal("Could not create socketpair to communicate with " 121f7167e0eSDag-Erling Smørgrav "proxy dialer: %.100s", strerror(errno)); 122f7167e0eSDag-Erling Smørgrav 123f7167e0eSDag-Erling Smørgrav command_string = expand_proxy_command(proxy_command, options.user, 124f7167e0eSDag-Erling Smørgrav host, port); 125f7167e0eSDag-Erling Smørgrav debug("Executing proxy dialer command: %.500s", command_string); 126f7167e0eSDag-Erling Smørgrav 127f7167e0eSDag-Erling Smørgrav /* Fork and execute the proxy command. */ 128f7167e0eSDag-Erling Smørgrav if ((pid = fork()) == 0) { 129f7167e0eSDag-Erling Smørgrav char *argv[10]; 130f7167e0eSDag-Erling Smørgrav 131f7167e0eSDag-Erling Smørgrav /* Child. Permanently give up superuser privileges. */ 132f7167e0eSDag-Erling Smørgrav permanently_drop_suid(original_real_uid); 133f7167e0eSDag-Erling Smørgrav 134f7167e0eSDag-Erling Smørgrav close(sp[1]); 135f7167e0eSDag-Erling Smørgrav /* Redirect stdin and stdout. */ 136f7167e0eSDag-Erling Smørgrav if (sp[0] != 0) { 137f7167e0eSDag-Erling Smørgrav if (dup2(sp[0], 0) < 0) 138f7167e0eSDag-Erling Smørgrav perror("dup2 stdin"); 139f7167e0eSDag-Erling Smørgrav } 140f7167e0eSDag-Erling Smørgrav if (sp[0] != 1) { 141f7167e0eSDag-Erling Smørgrav if (dup2(sp[0], 1) < 0) 142f7167e0eSDag-Erling Smørgrav perror("dup2 stdout"); 143f7167e0eSDag-Erling Smørgrav } 144f7167e0eSDag-Erling Smørgrav if (sp[0] >= 2) 145f7167e0eSDag-Erling Smørgrav close(sp[0]); 146f7167e0eSDag-Erling Smørgrav 147f7167e0eSDag-Erling Smørgrav /* 148f7167e0eSDag-Erling Smørgrav * Stderr is left as it is so that error messages get 149f7167e0eSDag-Erling Smørgrav * printed on the user's terminal. 150f7167e0eSDag-Erling Smørgrav */ 151f7167e0eSDag-Erling Smørgrav argv[0] = shell; 152f7167e0eSDag-Erling Smørgrav argv[1] = "-c"; 153f7167e0eSDag-Erling Smørgrav argv[2] = command_string; 154f7167e0eSDag-Erling Smørgrav argv[3] = NULL; 155f7167e0eSDag-Erling Smørgrav 156f7167e0eSDag-Erling Smørgrav /* 157f7167e0eSDag-Erling Smørgrav * Execute the proxy command. 158f7167e0eSDag-Erling Smørgrav * Note that we gave up any extra privileges above. 159f7167e0eSDag-Erling Smørgrav */ 160f7167e0eSDag-Erling Smørgrav execv(argv[0], argv); 161f7167e0eSDag-Erling Smørgrav perror(argv[0]); 162f7167e0eSDag-Erling Smørgrav exit(1); 163f7167e0eSDag-Erling Smørgrav } 164f7167e0eSDag-Erling Smørgrav /* Parent. */ 165f7167e0eSDag-Erling Smørgrav if (pid < 0) 166f7167e0eSDag-Erling Smørgrav fatal("fork failed: %.100s", strerror(errno)); 167f7167e0eSDag-Erling Smørgrav close(sp[0]); 168f7167e0eSDag-Erling Smørgrav free(command_string); 169f7167e0eSDag-Erling Smørgrav 170f7167e0eSDag-Erling Smørgrav if ((sock = mm_receive_fd(sp[1])) == -1) 171f7167e0eSDag-Erling Smørgrav fatal("proxy dialer did not pass back a connection"); 172*acc1a9efSDag-Erling Smørgrav close(sp[1]); 173f7167e0eSDag-Erling Smørgrav 174f7167e0eSDag-Erling Smørgrav while (waitpid(pid, NULL, 0) == -1) 175f7167e0eSDag-Erling Smørgrav if (errno != EINTR) 176f7167e0eSDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 177f7167e0eSDag-Erling Smørgrav 178f7167e0eSDag-Erling Smørgrav /* Set the connection file descriptors. */ 179f7167e0eSDag-Erling Smørgrav packet_set_connection(sock, sock); 180f7167e0eSDag-Erling Smørgrav 181f7167e0eSDag-Erling Smørgrav return 0; 182f7167e0eSDag-Erling Smørgrav } 183f7167e0eSDag-Erling Smørgrav 184511b41d2SMark Murray /* 185511b41d2SMark Murray * Connect to the given ssh server using a proxy command. 186511b41d2SMark Murray */ 187af12a3e7SDag-Erling Smørgrav static int 18880628bacSDag-Erling Smørgrav ssh_proxy_connect(const char *host, u_short port, 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 /* Child. Permanently give up superuser privileges. */ 212333ee039SDag-Erling Smørgrav permanently_drop_suid(original_real_uid); 213511b41d2SMark Murray 214511b41d2SMark Murray /* Redirect stdin and stdout. */ 215511b41d2SMark Murray close(pin[1]); 216511b41d2SMark Murray if (pin[0] != 0) { 217511b41d2SMark Murray if (dup2(pin[0], 0) < 0) 218511b41d2SMark Murray perror("dup2 stdin"); 219511b41d2SMark Murray close(pin[0]); 220511b41d2SMark Murray } 221511b41d2SMark Murray close(pout[0]); 222511b41d2SMark Murray if (dup2(pout[1], 1) < 0) 223511b41d2SMark Murray perror("dup2 stdout"); 224511b41d2SMark Murray /* Cannot be 1 because pin allocated two descriptors. */ 225511b41d2SMark Murray close(pout[1]); 226511b41d2SMark Murray 227511b41d2SMark Murray /* Stderr is left as it is so that error messages get 228511b41d2SMark Murray printed on the user's terminal. */ 229d4af9e69SDag-Erling Smørgrav argv[0] = shell; 230511b41d2SMark Murray argv[1] = "-c"; 231511b41d2SMark Murray argv[2] = command_string; 232511b41d2SMark Murray argv[3] = NULL; 233511b41d2SMark Murray 234511b41d2SMark Murray /* Execute the proxy command. Note that we gave up any 235511b41d2SMark Murray extra privileges above. */ 2364a421b63SDag-Erling Smørgrav signal(SIGPIPE, SIG_DFL); 237ca3176e7SBrian Feldman execv(argv[0], argv); 238ca3176e7SBrian Feldman perror(argv[0]); 239511b41d2SMark Murray exit(1); 240511b41d2SMark Murray } 241511b41d2SMark Murray /* Parent. */ 242511b41d2SMark Murray if (pid < 0) 243511b41d2SMark Murray fatal("fork failed: %.100s", strerror(errno)); 244f388f5efSDag-Erling Smørgrav else 245f388f5efSDag-Erling Smørgrav proxy_command_pid = pid; /* save pid to clean up later */ 246511b41d2SMark Murray 247511b41d2SMark Murray /* Close child side of the descriptors. */ 248511b41d2SMark Murray close(pin[0]); 249511b41d2SMark Murray close(pout[1]); 250511b41d2SMark Murray 251511b41d2SMark Murray /* Free the command name. */ 252e4a9863fSDag-Erling Smørgrav free(command_string); 253511b41d2SMark Murray 254511b41d2SMark Murray /* Set the connection file descriptors. */ 255511b41d2SMark Murray packet_set_connection(pout[0], pin[1]); 256511b41d2SMark Murray 257af12a3e7SDag-Erling Smørgrav /* Indicate OK return */ 258af12a3e7SDag-Erling Smørgrav return 0; 259511b41d2SMark Murray } 260511b41d2SMark Murray 2614a421b63SDag-Erling Smørgrav void 2624a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(void) 2634a421b63SDag-Erling Smørgrav { 2644a421b63SDag-Erling Smørgrav /* 2654a421b63SDag-Erling Smørgrav * Send SIGHUP to proxy command if used. We don't wait() in 2664a421b63SDag-Erling Smørgrav * case it hangs and instead rely on init to reap the child 2674a421b63SDag-Erling Smørgrav */ 2684a421b63SDag-Erling Smørgrav if (proxy_command_pid > 1) 2694a421b63SDag-Erling Smørgrav kill(proxy_command_pid, SIGHUP); 2704a421b63SDag-Erling Smørgrav } 2714a421b63SDag-Erling Smørgrav 272511b41d2SMark Murray /* 273511b41d2SMark Murray * Creates a (possibly privileged) socket for use as the ssh connection. 274511b41d2SMark Murray */ 275af12a3e7SDag-Erling Smørgrav static int 276cf2b5f3bSDag-Erling Smørgrav ssh_create_socket(int privileged, struct addrinfo *ai) 277511b41d2SMark Murray { 278f7167e0eSDag-Erling Smørgrav int sock, r, gaierr; 279b83788ffSDag-Erling Smørgrav struct addrinfo hints, *res = NULL; 280511b41d2SMark Murray 281cf2b5f3bSDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 282b15c8340SDag-Erling Smørgrav if (sock < 0) { 283f7167e0eSDag-Erling Smørgrav error("socket: %s", strerror(errno)); 284b15c8340SDag-Erling Smørgrav return -1; 285b15c8340SDag-Erling Smørgrav } 286b15c8340SDag-Erling Smørgrav fcntl(sock, F_SETFD, FD_CLOEXEC); 287af12a3e7SDag-Erling Smørgrav 288af12a3e7SDag-Erling Smørgrav /* Bind the socket to an alternative local IP address */ 289f7167e0eSDag-Erling Smørgrav if (options.bind_address == NULL && !privileged) 290af12a3e7SDag-Erling Smørgrav return sock; 291af12a3e7SDag-Erling Smørgrav 292b83788ffSDag-Erling Smørgrav if (options.bind_address) { 293af12a3e7SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 294cf2b5f3bSDag-Erling Smørgrav hints.ai_family = ai->ai_family; 295cf2b5f3bSDag-Erling Smørgrav hints.ai_socktype = ai->ai_socktype; 296cf2b5f3bSDag-Erling Smørgrav hints.ai_protocol = ai->ai_protocol; 297af12a3e7SDag-Erling Smørgrav hints.ai_flags = AI_PASSIVE; 298d4af9e69SDag-Erling Smørgrav gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); 299af12a3e7SDag-Erling Smørgrav if (gaierr) { 300af12a3e7SDag-Erling Smørgrav error("getaddrinfo: %s: %s", options.bind_address, 301d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 302af12a3e7SDag-Erling Smørgrav close(sock); 303af12a3e7SDag-Erling Smørgrav return -1; 304511b41d2SMark Murray } 305b83788ffSDag-Erling Smørgrav } 306f7167e0eSDag-Erling Smørgrav /* 307f7167e0eSDag-Erling Smørgrav * If we are running as root and want to connect to a privileged 308f7167e0eSDag-Erling Smørgrav * port, bind our own socket to a privileged port. 309f7167e0eSDag-Erling Smørgrav */ 310f7167e0eSDag-Erling Smørgrav if (privileged) { 311f7167e0eSDag-Erling Smørgrav PRIV_START; 312b83788ffSDag-Erling Smørgrav r = bindresvport_sa(sock, res ? res->ai_addr : NULL); 313f7167e0eSDag-Erling Smørgrav PRIV_END; 314f7167e0eSDag-Erling Smørgrav if (r < 0) { 315f7167e0eSDag-Erling Smørgrav error("bindresvport_sa: af=%d %s", ai->ai_family, 316f7167e0eSDag-Erling Smørgrav strerror(errno)); 317f7167e0eSDag-Erling Smørgrav goto fail; 318f7167e0eSDag-Erling Smørgrav } 319f7167e0eSDag-Erling Smørgrav } else { 320af12a3e7SDag-Erling Smørgrav if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 321f7167e0eSDag-Erling Smørgrav error("bind: %s: %s", options.bind_address, 322f7167e0eSDag-Erling Smørgrav strerror(errno)); 323f7167e0eSDag-Erling Smørgrav fail: 324af12a3e7SDag-Erling Smørgrav close(sock); 325af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 326af12a3e7SDag-Erling Smørgrav return -1; 327af12a3e7SDag-Erling Smørgrav } 328f7167e0eSDag-Erling Smørgrav } 329b83788ffSDag-Erling Smørgrav if (res != NULL) 330af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 331511b41d2SMark Murray return sock; 332511b41d2SMark Murray } 333511b41d2SMark Murray 334cf2b5f3bSDag-Erling Smørgrav static int 335cf2b5f3bSDag-Erling Smørgrav timeout_connect(int sockfd, const struct sockaddr *serv_addr, 336d4af9e69SDag-Erling Smørgrav socklen_t addrlen, int *timeoutp) 337cf2b5f3bSDag-Erling Smørgrav { 338cf2b5f3bSDag-Erling Smørgrav fd_set *fdset; 339d4af9e69SDag-Erling Smørgrav struct timeval tv, t_start; 340cf2b5f3bSDag-Erling Smørgrav socklen_t optlen; 341333ee039SDag-Erling Smørgrav int optval, rc, result = -1; 342cf2b5f3bSDag-Erling Smørgrav 343d4af9e69SDag-Erling Smørgrav gettimeofday(&t_start, NULL); 344d4af9e69SDag-Erling Smørgrav 345d4af9e69SDag-Erling Smørgrav if (*timeoutp <= 0) { 346d4af9e69SDag-Erling Smørgrav result = connect(sockfd, serv_addr, addrlen); 347d4af9e69SDag-Erling Smørgrav goto done; 348d4af9e69SDag-Erling Smørgrav } 349cf2b5f3bSDag-Erling Smørgrav 3501ec0d754SDag-Erling Smørgrav set_nonblock(sockfd); 351cf2b5f3bSDag-Erling Smørgrav rc = connect(sockfd, serv_addr, addrlen); 3521ec0d754SDag-Erling Smørgrav if (rc == 0) { 3531ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 354d4af9e69SDag-Erling Smørgrav result = 0; 355d4af9e69SDag-Erling Smørgrav goto done; 3561ec0d754SDag-Erling Smørgrav } 357d4af9e69SDag-Erling Smørgrav if (errno != EINPROGRESS) { 358d4af9e69SDag-Erling Smørgrav result = -1; 359d4af9e69SDag-Erling Smørgrav goto done; 360d4af9e69SDag-Erling Smørgrav } 361cf2b5f3bSDag-Erling Smørgrav 362fc1ba28aSDag-Erling Smørgrav fdset = xcalloc(howmany(sockfd + 1, NFDBITS), 363333ee039SDag-Erling Smørgrav sizeof(fd_mask)); 364cf2b5f3bSDag-Erling Smørgrav FD_SET(sockfd, fdset); 365d4af9e69SDag-Erling Smørgrav ms_to_timeval(&tv, *timeoutp); 366cf2b5f3bSDag-Erling Smørgrav 367cf2b5f3bSDag-Erling Smørgrav for (;;) { 368cf2b5f3bSDag-Erling Smørgrav rc = select(sockfd + 1, NULL, fdset, NULL, &tv); 369cf2b5f3bSDag-Erling Smørgrav if (rc != -1 || errno != EINTR) 370cf2b5f3bSDag-Erling Smørgrav break; 371cf2b5f3bSDag-Erling Smørgrav } 372cf2b5f3bSDag-Erling Smørgrav 373cf2b5f3bSDag-Erling Smørgrav switch (rc) { 374cf2b5f3bSDag-Erling Smørgrav case 0: 375cf2b5f3bSDag-Erling Smørgrav /* Timed out */ 376cf2b5f3bSDag-Erling Smørgrav errno = ETIMEDOUT; 377cf2b5f3bSDag-Erling Smørgrav break; 378cf2b5f3bSDag-Erling Smørgrav case -1: 379cf2b5f3bSDag-Erling Smørgrav /* Select error */ 380cf2b5f3bSDag-Erling Smørgrav debug("select: %s", strerror(errno)); 381cf2b5f3bSDag-Erling Smørgrav break; 382cf2b5f3bSDag-Erling Smørgrav case 1: 383cf2b5f3bSDag-Erling Smørgrav /* Completed or failed */ 384cf2b5f3bSDag-Erling Smørgrav optval = 0; 385cf2b5f3bSDag-Erling Smørgrav optlen = sizeof(optval); 386cf2b5f3bSDag-Erling Smørgrav if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, 387cf2b5f3bSDag-Erling Smørgrav &optlen) == -1) { 388cf2b5f3bSDag-Erling Smørgrav debug("getsockopt: %s", strerror(errno)); 389cf2b5f3bSDag-Erling Smørgrav break; 390cf2b5f3bSDag-Erling Smørgrav } 391cf2b5f3bSDag-Erling Smørgrav if (optval != 0) { 392cf2b5f3bSDag-Erling Smørgrav errno = optval; 393cf2b5f3bSDag-Erling Smørgrav break; 394cf2b5f3bSDag-Erling Smørgrav } 395cf2b5f3bSDag-Erling Smørgrav result = 0; 3961ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 397cf2b5f3bSDag-Erling Smørgrav break; 398cf2b5f3bSDag-Erling Smørgrav default: 399cf2b5f3bSDag-Erling Smørgrav /* Should not occur */ 400cf2b5f3bSDag-Erling Smørgrav fatal("Bogus return (%d) from select()", rc); 401cf2b5f3bSDag-Erling Smørgrav } 402cf2b5f3bSDag-Erling Smørgrav 403e4a9863fSDag-Erling Smørgrav free(fdset); 404d4af9e69SDag-Erling Smørgrav 405d4af9e69SDag-Erling Smørgrav done: 406d4af9e69SDag-Erling Smørgrav if (result == 0 && *timeoutp > 0) { 407d4af9e69SDag-Erling Smørgrav ms_subtract_diff(&t_start, timeoutp); 408d4af9e69SDag-Erling Smørgrav if (*timeoutp <= 0) { 409d4af9e69SDag-Erling Smørgrav errno = ETIMEDOUT; 410d4af9e69SDag-Erling Smørgrav result = -1; 411d4af9e69SDag-Erling Smørgrav } 412d4af9e69SDag-Erling Smørgrav } 413d4af9e69SDag-Erling Smørgrav 414cf2b5f3bSDag-Erling Smørgrav return (result); 415cf2b5f3bSDag-Erling Smørgrav } 416cf2b5f3bSDag-Erling Smørgrav 417511b41d2SMark Murray /* 418511b41d2SMark Murray * Opens a TCP/IP connection to the remote server on the given host. 419511b41d2SMark Murray * The address of the remote host will be returned in hostaddr. 42080628bacSDag-Erling Smørgrav * If port is 0, the default port will be used. If needpriv is true, 421511b41d2SMark Murray * a privileged port will be allocated to make the connection. 42280628bacSDag-Erling Smørgrav * This requires super-user privileges if needpriv is true. 423511b41d2SMark Murray * Connection_attempts specifies the maximum number of tries (one per 424511b41d2SMark Murray * second). If proxy_command is non-NULL, it specifies the command (with %h 425511b41d2SMark Murray * and %p substituted for host and port, respectively) to use to contact 426511b41d2SMark Murray * the daemon. 427511b41d2SMark Murray */ 428f7167e0eSDag-Erling Smørgrav static int 429f7167e0eSDag-Erling Smørgrav ssh_connect_direct(const char *host, struct addrinfo *aitop, 430f7167e0eSDag-Erling Smørgrav struct sockaddr_storage *hostaddr, u_short port, int family, 431f7167e0eSDag-Erling Smørgrav int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) 432511b41d2SMark Murray { 433ca3176e7SBrian Feldman int on = 1; 434ca3176e7SBrian Feldman int sock = -1, attempt; 435ca3176e7SBrian Feldman char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 436f7167e0eSDag-Erling Smørgrav struct addrinfo *ai; 437511b41d2SMark Murray 438*acc1a9efSDag-Erling Smørgrav debug2("%s: needpriv %d", __func__, needpriv); 439*acc1a9efSDag-Erling Smørgrav memset(ntop, 0, sizeof(ntop)); 440*acc1a9efSDag-Erling Smørgrav memset(strport, 0, sizeof(strport)); 441511b41d2SMark Murray 442333ee039SDag-Erling Smørgrav for (attempt = 0; attempt < connection_attempts; attempt++) { 44362efe23aSDag-Erling Smørgrav if (attempt > 0) { 44462efe23aSDag-Erling Smørgrav /* Sleep a moment before retrying. */ 44562efe23aSDag-Erling Smørgrav sleep(1); 446511b41d2SMark Murray debug("Trying again..."); 44762efe23aSDag-Erling Smørgrav } 448333ee039SDag-Erling Smørgrav /* 449333ee039SDag-Erling Smørgrav * Loop through addresses for this host, and try each one in 450333ee039SDag-Erling Smørgrav * sequence until the connection succeeds. 451333ee039SDag-Erling Smørgrav */ 452511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 453f7167e0eSDag-Erling Smørgrav if (ai->ai_family != AF_INET && 454f7167e0eSDag-Erling Smørgrav ai->ai_family != AF_INET6) 455511b41d2SMark Murray continue; 456511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 457511b41d2SMark Murray ntop, sizeof(ntop), strport, sizeof(strport), 458511b41d2SMark Murray NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 459*acc1a9efSDag-Erling Smørgrav error("%s: getnameinfo failed", __func__); 460511b41d2SMark Murray continue; 461511b41d2SMark Murray } 462511b41d2SMark Murray debug("Connecting to %.200s [%.100s] port %s.", 4631f5ce8f4SBrian Feldman host, ntop, strport); 464511b41d2SMark Murray 465511b41d2SMark Murray /* Create a socket for connecting. */ 466cf2b5f3bSDag-Erling Smørgrav sock = ssh_create_socket(needpriv, ai); 467511b41d2SMark Murray if (sock < 0) 468af12a3e7SDag-Erling Smørgrav /* Any error is already output */ 469511b41d2SMark Murray continue; 470511b41d2SMark Murray 471cf2b5f3bSDag-Erling Smørgrav if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 472d4af9e69SDag-Erling Smørgrav timeout_ms) >= 0) { 473511b41d2SMark Murray /* Successful connection. */ 474c322fe35SKris Kennaway memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 475511b41d2SMark Murray break; 476511b41d2SMark Murray } else { 477f388f5efSDag-Erling Smørgrav debug("connect to address %s port %s: %s", 478f388f5efSDag-Erling Smørgrav ntop, strport, strerror(errno)); 479511b41d2SMark Murray close(sock); 480333ee039SDag-Erling Smørgrav sock = -1; 481511b41d2SMark Murray } 482511b41d2SMark Murray } 483333ee039SDag-Erling Smørgrav if (sock != -1) 484511b41d2SMark Murray break; /* Successful connection. */ 485511b41d2SMark Murray } 486511b41d2SMark Murray 487511b41d2SMark Murray /* Return failure if we didn't get a successful connection. */ 488333ee039SDag-Erling Smørgrav if (sock == -1) { 489aa49c926SDag-Erling Smørgrav error("ssh: connect to host %s port %s: %s", 490f388f5efSDag-Erling Smørgrav host, strport, strerror(errno)); 491aa49c926SDag-Erling Smørgrav return (-1); 492f388f5efSDag-Erling Smørgrav } 493511b41d2SMark Murray 494511b41d2SMark Murray debug("Connection established."); 495511b41d2SMark Murray 4961ec0d754SDag-Erling Smørgrav /* Set SO_KEEPALIVE if requested. */ 497d4af9e69SDag-Erling Smørgrav if (want_keepalive && 498ca3176e7SBrian Feldman setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 499ca3176e7SBrian Feldman sizeof(on)) < 0) 500ca3176e7SBrian Feldman error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 501ca3176e7SBrian Feldman 502511b41d2SMark Murray /* Set the connection. */ 503511b41d2SMark Murray packet_set_connection(sock, sock); 504511b41d2SMark Murray 505af12a3e7SDag-Erling Smørgrav return 0; 506511b41d2SMark Murray } 507511b41d2SMark Murray 508f7167e0eSDag-Erling Smørgrav int 509f7167e0eSDag-Erling Smørgrav ssh_connect(const char *host, struct addrinfo *addrs, 510f7167e0eSDag-Erling Smørgrav struct sockaddr_storage *hostaddr, u_short port, int family, 511f7167e0eSDag-Erling Smørgrav int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) 512f7167e0eSDag-Erling Smørgrav { 513f7167e0eSDag-Erling Smørgrav if (options.proxy_command == NULL) { 514f7167e0eSDag-Erling Smørgrav return ssh_connect_direct(host, addrs, hostaddr, port, family, 515f7167e0eSDag-Erling Smørgrav connection_attempts, timeout_ms, want_keepalive, needpriv); 516f7167e0eSDag-Erling Smørgrav } else if (strcmp(options.proxy_command, "-") == 0) { 517f7167e0eSDag-Erling Smørgrav packet_set_connection(STDIN_FILENO, STDOUT_FILENO); 518f7167e0eSDag-Erling Smørgrav return 0; /* Always succeeds */ 519f7167e0eSDag-Erling Smørgrav } else if (options.proxy_use_fdpass) { 520f7167e0eSDag-Erling Smørgrav return ssh_proxy_fdpass_connect(host, port, 521f7167e0eSDag-Erling Smørgrav options.proxy_command); 522f7167e0eSDag-Erling Smørgrav } 523f7167e0eSDag-Erling Smørgrav return ssh_proxy_connect(host, port, options.proxy_command); 524f7167e0eSDag-Erling Smørgrav } 525f7167e0eSDag-Erling Smørgrav 5266888a9beSDag-Erling Smørgrav static void 5276888a9beSDag-Erling Smørgrav send_client_banner(int connection_out, int minor1) 5286888a9beSDag-Erling Smørgrav { 5296888a9beSDag-Erling Smørgrav /* Send our own protocol version identification. */ 53060c59fadSDag-Erling Smørgrav xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s%s", 5316888a9beSDag-Erling Smørgrav compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 5326888a9beSDag-Erling Smørgrav compat20 ? PROTOCOL_MINOR_2 : minor1, 53360c59fadSDag-Erling Smørgrav SSH_VERSION, 5346888a9beSDag-Erling Smørgrav *options.version_addendum == '\0' ? "" : " ", 5356888a9beSDag-Erling Smørgrav options.version_addendum, compat20 ? "\r\n" : "\n"); 536*acc1a9efSDag-Erling Smørgrav if (atomicio(vwrite, connection_out, client_version_string, 5376888a9beSDag-Erling Smørgrav strlen(client_version_string)) != strlen(client_version_string)) 5386888a9beSDag-Erling Smørgrav fatal("write: %.100s", strerror(errno)); 5396888a9beSDag-Erling Smørgrav chop(client_version_string); 5406888a9beSDag-Erling Smørgrav debug("Local version string %.100s", client_version_string); 5416888a9beSDag-Erling Smørgrav } 5426888a9beSDag-Erling Smørgrav 543511b41d2SMark Murray /* 544e8aafc91SKris Kennaway * Waits for the server identification string, and sends our own 545e8aafc91SKris Kennaway * identification string. 546511b41d2SMark Murray */ 5477aee6ffeSDag-Erling Smørgrav void 548d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(int timeout_ms) 549511b41d2SMark Murray { 550e8aafc91SKris Kennaway char buf[256], remote_version[256]; /* must be same size! */ 551d4ecd108SDag-Erling Smørgrav int remote_major, remote_minor, mismatch; 552e8aafc91SKris Kennaway int connection_in = packet_get_connection_in(); 553e8aafc91SKris Kennaway int connection_out = packet_get_connection_out(); 5546888a9beSDag-Erling Smørgrav int minor1 = PROTOCOL_MINOR_1, client_banner_sent = 0; 555333ee039SDag-Erling Smørgrav u_int i, n; 556d4af9e69SDag-Erling Smørgrav size_t len; 557d4af9e69SDag-Erling Smørgrav int fdsetsz, remaining, rc; 558d4af9e69SDag-Erling Smørgrav struct timeval t_start, t_remaining; 559d4af9e69SDag-Erling Smørgrav fd_set *fdset; 560d4af9e69SDag-Erling Smørgrav 561d4af9e69SDag-Erling Smørgrav fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); 562d4af9e69SDag-Erling Smørgrav fdset = xcalloc(1, fdsetsz); 563511b41d2SMark Murray 5646888a9beSDag-Erling Smørgrav /* 5656888a9beSDag-Erling Smørgrav * If we are SSH2-only then we can send the banner immediately and 5666888a9beSDag-Erling Smørgrav * save a round-trip. 5676888a9beSDag-Erling Smørgrav */ 5686888a9beSDag-Erling Smørgrav if (options.protocol == SSH_PROTO_2) { 5696888a9beSDag-Erling Smørgrav enable_compat20(); 5706888a9beSDag-Erling Smørgrav send_client_banner(connection_out, 0); 5716888a9beSDag-Erling Smørgrav client_banner_sent = 1; 5726888a9beSDag-Erling Smørgrav } 5736888a9beSDag-Erling Smørgrav 574d4ecd108SDag-Erling Smørgrav /* Read other side's version identification. */ 575d4af9e69SDag-Erling Smørgrav remaining = timeout_ms; 576333ee039SDag-Erling Smørgrav for (n = 0;;) { 577e8aafc91SKris Kennaway for (i = 0; i < sizeof(buf) - 1; i++) { 578d4af9e69SDag-Erling Smørgrav if (timeout_ms > 0) { 579d4af9e69SDag-Erling Smørgrav gettimeofday(&t_start, NULL); 580d4af9e69SDag-Erling Smørgrav ms_to_timeval(&t_remaining, remaining); 581d4af9e69SDag-Erling Smørgrav FD_SET(connection_in, fdset); 582d4af9e69SDag-Erling Smørgrav rc = select(connection_in + 1, fdset, NULL, 583d4af9e69SDag-Erling Smørgrav fdset, &t_remaining); 584d4af9e69SDag-Erling Smørgrav ms_subtract_diff(&t_start, &remaining); 585d4af9e69SDag-Erling Smørgrav if (rc == 0 || remaining <= 0) 586d4af9e69SDag-Erling Smørgrav fatal("Connection timed out during " 587d4af9e69SDag-Erling Smørgrav "banner exchange"); 588d4af9e69SDag-Erling Smørgrav if (rc == -1) { 589d4af9e69SDag-Erling Smørgrav if (errno == EINTR) 590d4af9e69SDag-Erling Smørgrav continue; 591d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 592d4af9e69SDag-Erling Smørgrav "select: %s", strerror(errno)); 593d4af9e69SDag-Erling Smørgrav } 594d4af9e69SDag-Erling Smørgrav } 595d4af9e69SDag-Erling Smørgrav 596*acc1a9efSDag-Erling Smørgrav len = atomicio(read, connection_in, &buf[i], 1); 597d4ecd108SDag-Erling Smørgrav 598d4ecd108SDag-Erling Smørgrav if (len != 1 && errno == EPIPE) 599d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 600d4af9e69SDag-Erling Smørgrav "Connection closed by remote host"); 601d4ecd108SDag-Erling Smørgrav else if (len != 1) 602d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 603d4af9e69SDag-Erling Smørgrav "read: %.100s", strerror(errno)); 604e8aafc91SKris Kennaway if (buf[i] == '\r') { 605e8aafc91SKris Kennaway buf[i] = '\n'; 606e8aafc91SKris Kennaway buf[i + 1] = 0; 607e8aafc91SKris Kennaway continue; /**XXX wait for \n */ 608511b41d2SMark Murray } 609e8aafc91SKris Kennaway if (buf[i] == '\n') { 610e8aafc91SKris Kennaway buf[i + 1] = 0; 611511b41d2SMark Murray break; 612e8aafc91SKris Kennaway } 613333ee039SDag-Erling Smørgrav if (++n > 65536) 614d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 615d4af9e69SDag-Erling Smørgrav "No banner received"); 616e8aafc91SKris Kennaway } 617e8aafc91SKris Kennaway buf[sizeof(buf) - 1] = 0; 618c2d3a559SKris Kennaway if (strncmp(buf, "SSH-", 4) == 0) 619c2d3a559SKris Kennaway break; 620c2d3a559SKris Kennaway debug("ssh_exchange_identification: %s", buf); 621c2d3a559SKris Kennaway } 622e8aafc91SKris Kennaway server_version_string = xstrdup(buf); 623e4a9863fSDag-Erling Smørgrav free(fdset); 624511b41d2SMark Murray 625511b41d2SMark Murray /* 626e8aafc91SKris Kennaway * Check that the versions match. In future this might accept 627e8aafc91SKris Kennaway * several versions and set appropriate flags to handle them. 628511b41d2SMark Murray */ 629e8aafc91SKris Kennaway if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 630e8aafc91SKris Kennaway &remote_major, &remote_minor, remote_version) != 3) 631e8aafc91SKris Kennaway fatal("Bad remote protocol version identification: '%.100s'", buf); 632e8aafc91SKris Kennaway debug("Remote protocol version %d.%d, remote software version %.100s", 633e8aafc91SKris Kennaway remote_major, remote_minor, remote_version); 634511b41d2SMark Murray 635bc5531deSDag-Erling Smørgrav active_state->compat = compat_datafellows(remote_version); 636e8aafc91SKris Kennaway mismatch = 0; 637e8aafc91SKris Kennaway 638e8aafc91SKris Kennaway switch (remote_major) { 639e8aafc91SKris Kennaway case 1: 640e8aafc91SKris Kennaway if (remote_minor == 99 && 641e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) && 642e8aafc91SKris Kennaway !(options.protocol & SSH_PROTO_1_PREFERRED)) { 643e8aafc91SKris Kennaway enable_compat20(); 644511b41d2SMark Murray break; 645e8aafc91SKris Kennaway } 646e8aafc91SKris Kennaway if (!(options.protocol & SSH_PROTO_1)) { 647e8aafc91SKris Kennaway mismatch = 1; 648e8aafc91SKris Kennaway break; 649e8aafc91SKris Kennaway } 650e8aafc91SKris Kennaway if (remote_minor < 3) { 651e8aafc91SKris Kennaway fatal("Remote machine has too old SSH software version."); 652ca3176e7SBrian Feldman } else if (remote_minor == 3 || remote_minor == 4) { 653e8aafc91SKris Kennaway /* We speak 1.3, too. */ 654e8aafc91SKris Kennaway enable_compat13(); 655ca3176e7SBrian Feldman minor1 = 3; 656e8aafc91SKris Kennaway if (options.forward_agent) { 657cf2b5f3bSDag-Erling Smørgrav logit("Agent forwarding disabled for protocol 1.3"); 658e8aafc91SKris Kennaway options.forward_agent = 0; 659e8aafc91SKris Kennaway } 660e8aafc91SKris Kennaway } 661e8aafc91SKris Kennaway break; 662e8aafc91SKris Kennaway case 2: 663e8aafc91SKris Kennaway if (options.protocol & SSH_PROTO_2) { 664e8aafc91SKris Kennaway enable_compat20(); 665e8aafc91SKris Kennaway break; 666e8aafc91SKris Kennaway } 667e8aafc91SKris Kennaway /* FALLTHROUGH */ 668511b41d2SMark Murray default: 669e8aafc91SKris Kennaway mismatch = 1; 670e8aafc91SKris Kennaway break; 671511b41d2SMark Murray } 672e8aafc91SKris Kennaway if (mismatch) 673e8aafc91SKris Kennaway fatal("Protocol major versions differ: %d vs. %d", 674e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 675e8aafc91SKris Kennaway remote_major); 676f7167e0eSDag-Erling Smørgrav if ((datafellows & SSH_BUG_DERIVEKEY) != 0) 677f7167e0eSDag-Erling Smørgrav fatal("Server version \"%.100s\" uses unsafe key agreement; " 678f7167e0eSDag-Erling Smørgrav "refusing connection", remote_version); 679f7167e0eSDag-Erling Smørgrav if ((datafellows & SSH_BUG_RSASIGMD5) != 0) 680f7167e0eSDag-Erling Smørgrav logit("Server version \"%.100s\" uses unsafe RSA signature " 681f7167e0eSDag-Erling Smørgrav "scheme; disabling use of RSA keys", remote_version); 6826888a9beSDag-Erling Smørgrav if (!client_banner_sent) 6836888a9beSDag-Erling Smørgrav send_client_banner(connection_out, minor1); 684e8aafc91SKris Kennaway chop(server_version_string); 685511b41d2SMark Murray } 686511b41d2SMark Murray 687ca3176e7SBrian Feldman /* defaults to 'no' */ 688af12a3e7SDag-Erling Smørgrav static int 689af12a3e7SDag-Erling Smørgrav confirm(const char *prompt) 690511b41d2SMark Murray { 691af12a3e7SDag-Erling Smørgrav const char *msg, *again = "Please type 'yes' or 'no': "; 692af12a3e7SDag-Erling Smørgrav char *p; 693af12a3e7SDag-Erling Smørgrav int ret = -1; 694511b41d2SMark Murray 695ca3176e7SBrian Feldman if (options.batch_mode) 696ca3176e7SBrian Feldman return 0; 697af12a3e7SDag-Erling Smørgrav for (msg = prompt;;msg = again) { 698af12a3e7SDag-Erling Smørgrav p = read_passphrase(msg, RP_ECHO); 699af12a3e7SDag-Erling Smørgrav if (p == NULL || 700af12a3e7SDag-Erling Smørgrav (p[0] == '\0') || (p[0] == '\n') || 701af12a3e7SDag-Erling Smørgrav strncasecmp(p, "no", 2) == 0) 702af12a3e7SDag-Erling Smørgrav ret = 0; 703f388f5efSDag-Erling Smørgrav if (p && strncasecmp(p, "yes", 3) == 0) 704af12a3e7SDag-Erling Smørgrav ret = 1; 705e4a9863fSDag-Erling Smørgrav free(p); 706af12a3e7SDag-Erling Smørgrav if (ret != -1) 707af12a3e7SDag-Erling Smørgrav return ret; 708511b41d2SMark Murray } 709511b41d2SMark Murray } 710511b41d2SMark Murray 711b15c8340SDag-Erling Smørgrav static int 712b15c8340SDag-Erling Smørgrav check_host_cert(const char *host, const Key *host_key) 713b15c8340SDag-Erling Smørgrav { 714b15c8340SDag-Erling Smørgrav const char *reason; 715b15c8340SDag-Erling Smørgrav 716b15c8340SDag-Erling Smørgrav if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { 717b15c8340SDag-Erling Smørgrav error("%s", reason); 718b15c8340SDag-Erling Smørgrav return 0; 719b15c8340SDag-Erling Smørgrav } 720a0ee8cc6SDag-Erling Smørgrav if (buffer_len(host_key->cert->critical) != 0) { 721e2f6069cSDag-Erling Smørgrav error("Certificate for %s contains unsupported " 722e2f6069cSDag-Erling Smørgrav "critical options(s)", host); 723b15c8340SDag-Erling Smørgrav return 0; 724b15c8340SDag-Erling Smørgrav } 725b15c8340SDag-Erling Smørgrav return 1; 726b15c8340SDag-Erling Smørgrav } 727b15c8340SDag-Erling Smørgrav 7284a421b63SDag-Erling Smørgrav static int 7294a421b63SDag-Erling Smørgrav sockaddr_is_local(struct sockaddr *hostaddr) 7304a421b63SDag-Erling Smørgrav { 7314a421b63SDag-Erling Smørgrav switch (hostaddr->sa_family) { 7324a421b63SDag-Erling Smørgrav case AF_INET: 7334a421b63SDag-Erling Smørgrav return (ntohl(((struct sockaddr_in *)hostaddr)-> 7344a421b63SDag-Erling Smørgrav sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 7354a421b63SDag-Erling Smørgrav case AF_INET6: 7364a421b63SDag-Erling Smørgrav return IN6_IS_ADDR_LOOPBACK( 7374a421b63SDag-Erling Smørgrav &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 7384a421b63SDag-Erling Smørgrav default: 7394a421b63SDag-Erling Smørgrav return 0; 7404a421b63SDag-Erling Smørgrav } 7414a421b63SDag-Erling Smørgrav } 7424a421b63SDag-Erling Smørgrav 7434a421b63SDag-Erling Smørgrav /* 7444a421b63SDag-Erling Smørgrav * Prepare the hostname and ip address strings that are used to lookup 7454a421b63SDag-Erling Smørgrav * host keys in known_hosts files. These may have a port number appended. 7464a421b63SDag-Erling Smørgrav */ 7474a421b63SDag-Erling Smørgrav void 7484a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, 7494a421b63SDag-Erling Smørgrav u_short port, char **hostfile_hostname, char **hostfile_ipaddr) 7504a421b63SDag-Erling Smørgrav { 7514a421b63SDag-Erling Smørgrav char ntop[NI_MAXHOST]; 7524a421b63SDag-Erling Smørgrav socklen_t addrlen; 7534a421b63SDag-Erling Smørgrav 7544a421b63SDag-Erling Smørgrav switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { 7554a421b63SDag-Erling Smørgrav case -1: 7564a421b63SDag-Erling Smørgrav addrlen = 0; 7574a421b63SDag-Erling Smørgrav break; 7584a421b63SDag-Erling Smørgrav case AF_INET: 7594a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr_in); 7604a421b63SDag-Erling Smørgrav break; 7614a421b63SDag-Erling Smørgrav case AF_INET6: 7624a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr_in6); 7634a421b63SDag-Erling Smørgrav break; 7644a421b63SDag-Erling Smørgrav default: 7654a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr); 7664a421b63SDag-Erling Smørgrav break; 7674a421b63SDag-Erling Smørgrav } 7684a421b63SDag-Erling Smørgrav 7694a421b63SDag-Erling Smørgrav /* 7704a421b63SDag-Erling Smørgrav * We don't have the remote ip-address for connections 7714a421b63SDag-Erling Smørgrav * using a proxy command 7724a421b63SDag-Erling Smørgrav */ 7734a421b63SDag-Erling Smørgrav if (hostfile_ipaddr != NULL) { 7744a421b63SDag-Erling Smørgrav if (options.proxy_command == NULL) { 7754a421b63SDag-Erling Smørgrav if (getnameinfo(hostaddr, addrlen, 7764a421b63SDag-Erling Smørgrav ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) 777bc5531deSDag-Erling Smørgrav fatal("%s: getnameinfo failed", __func__); 7784a421b63SDag-Erling Smørgrav *hostfile_ipaddr = put_host_port(ntop, port); 7794a421b63SDag-Erling Smørgrav } else { 7804a421b63SDag-Erling Smørgrav *hostfile_ipaddr = xstrdup("<no hostip for proxy " 7814a421b63SDag-Erling Smørgrav "command>"); 7824a421b63SDag-Erling Smørgrav } 7834a421b63SDag-Erling Smørgrav } 7844a421b63SDag-Erling Smørgrav 7854a421b63SDag-Erling Smørgrav /* 7864a421b63SDag-Erling Smørgrav * Allow the user to record the key under a different name or 7874a421b63SDag-Erling Smørgrav * differentiate a non-standard port. This is useful for ssh 7884a421b63SDag-Erling Smørgrav * tunneling over forwarded connections or if you run multiple 7894a421b63SDag-Erling Smørgrav * sshd's on different ports on the same machine. 7904a421b63SDag-Erling Smørgrav */ 7914a421b63SDag-Erling Smørgrav if (hostfile_hostname != NULL) { 7924a421b63SDag-Erling Smørgrav if (options.host_key_alias != NULL) { 7934a421b63SDag-Erling Smørgrav *hostfile_hostname = xstrdup(options.host_key_alias); 7944a421b63SDag-Erling Smørgrav debug("using hostkeyalias: %s", *hostfile_hostname); 7954a421b63SDag-Erling Smørgrav } else { 7964a421b63SDag-Erling Smørgrav *hostfile_hostname = put_host_port(hostname, port); 7974a421b63SDag-Erling Smørgrav } 7984a421b63SDag-Erling Smørgrav } 7994a421b63SDag-Erling Smørgrav } 8004a421b63SDag-Erling Smørgrav 801e8aafc91SKris Kennaway /* 802af12a3e7SDag-Erling Smørgrav * check whether the supplied host key is valid, return -1 if the key 803e146993eSDag-Erling Smørgrav * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. 804e8aafc91SKris Kennaway */ 805333ee039SDag-Erling Smørgrav #define RDRW 0 806333ee039SDag-Erling Smørgrav #define RDONLY 1 807333ee039SDag-Erling Smørgrav #define ROQUIET 2 808af12a3e7SDag-Erling Smørgrav static int 809333ee039SDag-Erling Smørgrav check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 810e146993eSDag-Erling Smørgrav Key *host_key, int readonly, 811e146993eSDag-Erling Smørgrav char **user_hostfiles, u_int num_user_hostfiles, 812e146993eSDag-Erling Smørgrav char **system_hostfiles, u_int num_system_hostfiles) 813511b41d2SMark Murray { 814e8aafc91SKris Kennaway HostStatus host_status; 815e8aafc91SKris Kennaway HostStatus ip_status; 816e146993eSDag-Erling Smørgrav Key *raw_key = NULL; 817e146993eSDag-Erling Smørgrav char *ip = NULL, *host = NULL; 818e146993eSDag-Erling Smørgrav char hostline[1000], *hostp, *fp, *ra; 819af12a3e7SDag-Erling Smørgrav char msg[1024]; 820e146993eSDag-Erling Smørgrav const char *type; 8214a421b63SDag-Erling Smørgrav const struct hostkey_entry *host_found, *ip_found; 822e146993eSDag-Erling Smørgrav int len, cancelled_forwarding = 0; 823e146993eSDag-Erling Smørgrav int local = sockaddr_is_local(hostaddr); 824e146993eSDag-Erling Smørgrav int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; 825bc5531deSDag-Erling Smørgrav int hostkey_trusted = 0; /* Known or explicitly accepted by user */ 826e146993eSDag-Erling Smørgrav struct hostkeys *host_hostkeys, *ip_hostkeys; 827e146993eSDag-Erling Smørgrav u_int i; 828511b41d2SMark Murray 829e8aafc91SKris Kennaway /* 830e8aafc91SKris Kennaway * Force accepting of the host key for loopback/localhost. The 831e8aafc91SKris Kennaway * problem is that if the home directory is NFS-mounted to multiple 832e8aafc91SKris Kennaway * machines, localhost will refer to a different machine in each of 833e8aafc91SKris Kennaway * them, and the user will get bogus HOST_CHANGED warnings. This 834e8aafc91SKris Kennaway * essentially disables host authentication for localhost; however, 835e8aafc91SKris Kennaway * this is probably not a real problem. 836e8aafc91SKris Kennaway */ 837af12a3e7SDag-Erling Smørgrav if (options.no_host_authentication_for_localhost == 1 && local && 838af12a3e7SDag-Erling Smørgrav options.host_key_alias == NULL) { 839ca3176e7SBrian Feldman debug("Forcing accepting of host key for " 840ca3176e7SBrian Feldman "loopback/localhost."); 841af12a3e7SDag-Erling Smørgrav return 0; 842511b41d2SMark Murray } 843511b41d2SMark Murray 844e8aafc91SKris Kennaway /* 8454a421b63SDag-Erling Smørgrav * Prepare the hostname and address strings used for hostkey lookup. 8464a421b63SDag-Erling Smørgrav * In some cases, these will have a port number appended. 847e8aafc91SKris Kennaway */ 8484a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); 849d4af9e69SDag-Erling Smørgrav 850ca3176e7SBrian Feldman /* 851ca3176e7SBrian Feldman * Turn off check_host_ip if the connection is to localhost, via proxy 852ca3176e7SBrian Feldman * command or if we don't have a hostname to compare with 853ca3176e7SBrian Feldman */ 854333ee039SDag-Erling Smørgrav if (options.check_host_ip && (local || 855333ee039SDag-Erling Smørgrav strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 856ca3176e7SBrian Feldman options.check_host_ip = 0; 857ca3176e7SBrian Feldman 8584a421b63SDag-Erling Smørgrav host_hostkeys = init_hostkeys(); 859e146993eSDag-Erling Smørgrav for (i = 0; i < num_user_hostfiles; i++) 860e146993eSDag-Erling Smørgrav load_hostkeys(host_hostkeys, host, user_hostfiles[i]); 861e146993eSDag-Erling Smørgrav for (i = 0; i < num_system_hostfiles; i++) 862e146993eSDag-Erling Smørgrav load_hostkeys(host_hostkeys, host, system_hostfiles[i]); 8634a421b63SDag-Erling Smørgrav 8644a421b63SDag-Erling Smørgrav ip_hostkeys = NULL; 8654a421b63SDag-Erling Smørgrav if (!want_cert && options.check_host_ip) { 8664a421b63SDag-Erling Smørgrav ip_hostkeys = init_hostkeys(); 867e146993eSDag-Erling Smørgrav for (i = 0; i < num_user_hostfiles; i++) 868e146993eSDag-Erling Smørgrav load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]); 869e146993eSDag-Erling Smørgrav for (i = 0; i < num_system_hostfiles; i++) 870e146993eSDag-Erling Smørgrav load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]); 871e8aafc91SKris Kennaway } 872e8aafc91SKris Kennaway 873b15c8340SDag-Erling Smørgrav retry: 8744a421b63SDag-Erling Smørgrav /* Reload these as they may have changed on cert->key downgrade */ 875b15c8340SDag-Erling Smørgrav want_cert = key_is_cert(host_key); 876b15c8340SDag-Erling Smørgrav type = key_type(host_key); 877b15c8340SDag-Erling Smørgrav 878e8aafc91SKris Kennaway /* 879b74df5b2SDag-Erling Smørgrav * Check if the host key is present in the user's list of known 880e8aafc91SKris Kennaway * hosts or in the systemwide list. 881e8aafc91SKris Kennaway */ 8824a421b63SDag-Erling Smørgrav host_status = check_key_in_hostkeys(host_hostkeys, host_key, 8834a421b63SDag-Erling Smørgrav &host_found); 8844a421b63SDag-Erling Smørgrav 885e8aafc91SKris Kennaway /* 886e8aafc91SKris Kennaway * Also perform check for the ip address, skip the check if we are 887b15c8340SDag-Erling Smørgrav * localhost, looking for a certificate, or the hostname was an ip 888b15c8340SDag-Erling Smørgrav * address to begin with. 889e8aafc91SKris Kennaway */ 8904a421b63SDag-Erling Smørgrav if (!want_cert && ip_hostkeys != NULL) { 8914a421b63SDag-Erling Smørgrav ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, 8924a421b63SDag-Erling Smørgrav &ip_found); 893e8aafc91SKris Kennaway if (host_status == HOST_CHANGED && 8944a421b63SDag-Erling Smørgrav (ip_status != HOST_CHANGED || 8954a421b63SDag-Erling Smørgrav (ip_found != NULL && 8964a421b63SDag-Erling Smørgrav !key_equal(ip_found->key, host_found->key)))) 897e8aafc91SKris Kennaway host_ip_differ = 1; 898e8aafc91SKris Kennaway } else 899e8aafc91SKris Kennaway ip_status = host_status; 900e8aafc91SKris Kennaway 901e8aafc91SKris Kennaway switch (host_status) { 902e8aafc91SKris Kennaway case HOST_OK: 903e8aafc91SKris Kennaway /* The host is known and the key matches. */ 904b15c8340SDag-Erling Smørgrav debug("Host '%.200s' is known and matches the %s host %s.", 905b15c8340SDag-Erling Smørgrav host, type, want_cert ? "certificate" : "key"); 9064a421b63SDag-Erling Smørgrav debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", 9074a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 908b15c8340SDag-Erling Smørgrav if (want_cert && !check_host_cert(hostname, host_key)) 909b15c8340SDag-Erling Smørgrav goto fail; 910ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 911b15c8340SDag-Erling Smørgrav if (readonly || want_cert) 912cf2b5f3bSDag-Erling Smørgrav logit("%s host key for IP address " 913af12a3e7SDag-Erling Smørgrav "'%.128s' not in list of known hosts.", 914e8aafc91SKris Kennaway type, ip); 915e146993eSDag-Erling Smørgrav else if (!add_host_to_hostfile(user_hostfiles[0], ip, 916aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts)) 917cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the %s host key for IP " 918af12a3e7SDag-Erling Smørgrav "address '%.128s' to the list of known " 919557f75e5SDag-Erling Smørgrav "hosts (%.500s).", type, ip, 920e146993eSDag-Erling Smørgrav user_hostfiles[0]); 921af12a3e7SDag-Erling Smørgrav else 922cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added the %s host " 923af12a3e7SDag-Erling Smørgrav "key for IP address '%.128s' to the list " 924af12a3e7SDag-Erling Smørgrav "of known hosts.", type, ip); 925d4af9e69SDag-Erling Smørgrav } else if (options.visual_host_key) { 926bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(host_key, 927bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT); 928bc5531deSDag-Erling Smørgrav ra = sshkey_fingerprint(host_key, 929bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_RANDOMART); 930bc5531deSDag-Erling Smørgrav if (fp == NULL || ra == NULL) 931bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint fail", __func__); 932*acc1a9efSDag-Erling Smørgrav logit("Host key fingerprint is %s\n%s", fp, ra); 933e4a9863fSDag-Erling Smørgrav free(ra); 934e4a9863fSDag-Erling Smørgrav free(fp); 935e8aafc91SKris Kennaway } 936bc5531deSDag-Erling Smørgrav hostkey_trusted = 1; 937e8aafc91SKris Kennaway break; 938e8aafc91SKris Kennaway case HOST_NEW: 939333ee039SDag-Erling Smørgrav if (options.host_key_alias == NULL && port != 0 && 940333ee039SDag-Erling Smørgrav port != SSH_DEFAULT_PORT) { 941333ee039SDag-Erling Smørgrav debug("checking without port identifier"); 942cce7d346SDag-Erling Smørgrav if (check_host_key(hostname, hostaddr, 0, host_key, 943e146993eSDag-Erling Smørgrav ROQUIET, user_hostfiles, num_user_hostfiles, 944e146993eSDag-Erling Smørgrav system_hostfiles, num_system_hostfiles) == 0) { 945333ee039SDag-Erling Smørgrav debug("found matching key w/out port"); 946333ee039SDag-Erling Smørgrav break; 947333ee039SDag-Erling Smørgrav } 948333ee039SDag-Erling Smørgrav } 949b15c8340SDag-Erling Smørgrav if (readonly || want_cert) 950af12a3e7SDag-Erling Smørgrav goto fail; 951e8aafc91SKris Kennaway /* The host is new. */ 952e8aafc91SKris Kennaway if (options.strict_host_key_checking == 1) { 953af12a3e7SDag-Erling Smørgrav /* 954af12a3e7SDag-Erling Smørgrav * User has requested strict host key checking. We 955af12a3e7SDag-Erling Smørgrav * will not add the host key automatically. The only 956af12a3e7SDag-Erling Smørgrav * alternative left is to abort. 957af12a3e7SDag-Erling Smørgrav */ 958af12a3e7SDag-Erling Smørgrav error("No %s host key is known for %.200s and you " 959af12a3e7SDag-Erling Smørgrav "have requested strict checking.", type, host); 960af12a3e7SDag-Erling Smørgrav goto fail; 961e8aafc91SKris Kennaway } else if (options.strict_host_key_checking == 2) { 962cf2b5f3bSDag-Erling Smørgrav char msg1[1024], msg2[1024]; 963cf2b5f3bSDag-Erling Smørgrav 9644a421b63SDag-Erling Smørgrav if (show_other_keys(host_hostkeys, host_key)) 965cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), 966cf2b5f3bSDag-Erling Smørgrav "\nbut keys of different type are already" 967cf2b5f3bSDag-Erling Smørgrav " known for this host."); 968cf2b5f3bSDag-Erling Smørgrav else 969cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), "."); 970e8aafc91SKris Kennaway /* The default */ 971bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(host_key, 972bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT); 973bc5531deSDag-Erling Smørgrav ra = sshkey_fingerprint(host_key, 974bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_RANDOMART); 975bc5531deSDag-Erling Smørgrav if (fp == NULL || ra == NULL) 976bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint fail", __func__); 977cf2b5f3bSDag-Erling Smørgrav msg2[0] = '\0'; 978cf2b5f3bSDag-Erling Smørgrav if (options.verify_host_key_dns) { 9791ec0d754SDag-Erling Smørgrav if (matching_host_key_dns) 980cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 981cf2b5f3bSDag-Erling Smørgrav "Matching host key fingerprint" 982cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 983cf2b5f3bSDag-Erling Smørgrav else 984cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 985cf2b5f3bSDag-Erling Smørgrav "No matching host key fingerprint" 986cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 987cf2b5f3bSDag-Erling Smørgrav } 988af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 989af12a3e7SDag-Erling Smørgrav "The authenticity of host '%.200s (%s)' can't be " 990f388f5efSDag-Erling Smørgrav "established%s\n" 991d4af9e69SDag-Erling Smørgrav "%s key fingerprint is %s.%s%s\n%s" 992af12a3e7SDag-Erling Smørgrav "Are you sure you want to continue connecting " 993f388f5efSDag-Erling Smørgrav "(yes/no)? ", 994d4af9e69SDag-Erling Smørgrav host, ip, msg1, type, fp, 995d4af9e69SDag-Erling Smørgrav options.visual_host_key ? "\n" : "", 996d4af9e69SDag-Erling Smørgrav options.visual_host_key ? ra : "", 997d4af9e69SDag-Erling Smørgrav msg2); 998e4a9863fSDag-Erling Smørgrav free(ra); 999e4a9863fSDag-Erling Smørgrav free(fp); 1000af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 1001af12a3e7SDag-Erling Smørgrav goto fail; 1002bc5531deSDag-Erling Smørgrav hostkey_trusted = 1; /* user explicitly confirmed */ 1003e8aafc91SKris Kennaway } 1004af12a3e7SDag-Erling Smørgrav /* 1005af12a3e7SDag-Erling Smørgrav * If not in strict mode, add the key automatically to the 1006af12a3e7SDag-Erling Smørgrav * local known_hosts file. 1007af12a3e7SDag-Erling Smørgrav */ 1008aa49c926SDag-Erling Smørgrav if (options.check_host_ip && ip_status == HOST_NEW) { 10094a421b63SDag-Erling Smørgrav snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 1010aa49c926SDag-Erling Smørgrav hostp = hostline; 1011aa49c926SDag-Erling Smørgrav if (options.hash_known_hosts) { 1012aa49c926SDag-Erling Smørgrav /* Add hash of host and IP separately */ 1013e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], 1014e146993eSDag-Erling Smørgrav host, host_key, options.hash_known_hosts) && 1015e146993eSDag-Erling Smørgrav add_host_to_hostfile(user_hostfiles[0], ip, 1016aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts); 1017aa49c926SDag-Erling Smørgrav } else { 1018aa49c926SDag-Erling Smørgrav /* Add unhashed "host,ip" */ 1019e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], 1020aa49c926SDag-Erling Smørgrav hostline, host_key, 1021aa49c926SDag-Erling Smørgrav options.hash_known_hosts); 1022aa49c926SDag-Erling Smørgrav } 1023aa49c926SDag-Erling Smørgrav } else { 1024e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], host, 1025e146993eSDag-Erling Smørgrav host_key, options.hash_known_hosts); 1026aa49c926SDag-Erling Smørgrav hostp = host; 1027aa49c926SDag-Erling Smørgrav } 1028aa49c926SDag-Erling Smørgrav 1029aa49c926SDag-Erling Smørgrav if (!r) 1030cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the host to the list of known " 1031e146993eSDag-Erling Smørgrav "hosts (%.500s).", user_hostfiles[0]); 1032e8aafc91SKris Kennaway else 1033cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added '%.200s' (%s) to the " 1034af12a3e7SDag-Erling Smørgrav "list of known hosts.", hostp, type); 1035e8aafc91SKris Kennaway break; 1036b15c8340SDag-Erling Smørgrav case HOST_REVOKED: 1037b15c8340SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1038b15c8340SDag-Erling Smørgrav error("@ WARNING: REVOKED HOST KEY DETECTED! @"); 1039b15c8340SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1040b15c8340SDag-Erling Smørgrav error("The %s host key for %s is marked as revoked.", type, host); 1041b15c8340SDag-Erling Smørgrav error("This could mean that a stolen key is being used to"); 1042b15c8340SDag-Erling Smørgrav error("impersonate this host."); 1043b15c8340SDag-Erling Smørgrav 1044b15c8340SDag-Erling Smørgrav /* 1045b15c8340SDag-Erling Smørgrav * If strict host key checking is in use, the user will have 1046b15c8340SDag-Erling Smørgrav * to edit the key manually and we can only abort. 1047b15c8340SDag-Erling Smørgrav */ 1048b15c8340SDag-Erling Smørgrav if (options.strict_host_key_checking) { 1049b15c8340SDag-Erling Smørgrav error("%s host key for %.200s was revoked and you have " 1050b15c8340SDag-Erling Smørgrav "requested strict checking.", type, host); 1051b15c8340SDag-Erling Smørgrav goto fail; 1052b15c8340SDag-Erling Smørgrav } 1053b15c8340SDag-Erling Smørgrav goto continue_unsafe; 1054b15c8340SDag-Erling Smørgrav 1055e8aafc91SKris Kennaway case HOST_CHANGED: 1056b15c8340SDag-Erling Smørgrav if (want_cert) { 1057b15c8340SDag-Erling Smørgrav /* 1058b15c8340SDag-Erling Smørgrav * This is only a debug() since it is valid to have 1059b15c8340SDag-Erling Smørgrav * CAs with wildcard DNS matches that don't match 1060b15c8340SDag-Erling Smørgrav * all hosts that one might visit. 1061b15c8340SDag-Erling Smørgrav */ 1062b15c8340SDag-Erling Smørgrav debug("Host certificate authority does not " 10634a421b63SDag-Erling Smørgrav "match %s in %s:%lu", CA_MARKER, 10644a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1065b15c8340SDag-Erling Smørgrav goto fail; 1066b15c8340SDag-Erling Smørgrav } 1067333ee039SDag-Erling Smørgrav if (readonly == ROQUIET) 1068333ee039SDag-Erling Smørgrav goto fail; 1069e8aafc91SKris Kennaway if (options.check_host_ip && host_ip_differ) { 107021e764dfSDag-Erling Smørgrav char *key_msg; 1071e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 107221e764dfSDag-Erling Smørgrav key_msg = "is unknown"; 1073e8aafc91SKris Kennaway else if (ip_status == HOST_OK) 107421e764dfSDag-Erling Smørgrav key_msg = "is unchanged"; 1075e8aafc91SKris Kennaway else 107621e764dfSDag-Erling Smørgrav key_msg = "has a different value"; 1077e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1078e8aafc91SKris Kennaway error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 1079e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1080e8aafc91SKris Kennaway error("The %s host key for %s has changed,", type, host); 1081d4af9e69SDag-Erling Smørgrav error("and the key for the corresponding IP address %s", ip); 108221e764dfSDag-Erling Smørgrav error("%s. This could either mean that", key_msg); 1083e8aafc91SKris Kennaway error("DNS SPOOFING is happening or the IP address for the host"); 1084ca3176e7SBrian Feldman error("and its host key have changed at the same time."); 1085ca3176e7SBrian Feldman if (ip_status != HOST_NEW) 10864a421b63SDag-Erling Smørgrav error("Offending key for IP in %s:%lu", 10874a421b63SDag-Erling Smørgrav ip_found->file, ip_found->line); 1088e8aafc91SKris Kennaway } 1089e8aafc91SKris Kennaway /* The host key has changed. */ 10901ec0d754SDag-Erling Smørgrav warn_changed_key(host_key); 1091e8aafc91SKris Kennaway error("Add correct host key in %.100s to get rid of this message.", 1092e146993eSDag-Erling Smørgrav user_hostfiles[0]); 10934a421b63SDag-Erling Smørgrav error("Offending %s key in %s:%lu", key_type(host_found->key), 10944a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1095e8aafc91SKris Kennaway 1096e8aafc91SKris Kennaway /* 1097e8aafc91SKris Kennaway * If strict host key checking is in use, the user will have 1098e8aafc91SKris Kennaway * to edit the key manually and we can only abort. 1099e8aafc91SKris Kennaway */ 1100af12a3e7SDag-Erling Smørgrav if (options.strict_host_key_checking) { 1101af12a3e7SDag-Erling Smørgrav error("%s host key for %.200s has changed and you have " 1102af12a3e7SDag-Erling Smørgrav "requested strict checking.", type, host); 1103af12a3e7SDag-Erling Smørgrav goto fail; 1104af12a3e7SDag-Erling Smørgrav } 1105e8aafc91SKris Kennaway 1106b15c8340SDag-Erling Smørgrav continue_unsafe: 1107e8aafc91SKris Kennaway /* 1108e8aafc91SKris Kennaway * If strict host key checking has not been requested, allow 1109cf2b5f3bSDag-Erling Smørgrav * the connection but without MITM-able authentication or 1110333ee039SDag-Erling Smørgrav * forwarding. 1111e8aafc91SKris Kennaway */ 1112e8aafc91SKris Kennaway if (options.password_authentication) { 1113af12a3e7SDag-Erling Smørgrav error("Password authentication is disabled to avoid " 1114af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1115e8aafc91SKris Kennaway options.password_authentication = 0; 1116d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1117e8aafc91SKris Kennaway } 1118cf2b5f3bSDag-Erling Smørgrav if (options.kbd_interactive_authentication) { 1119cf2b5f3bSDag-Erling Smørgrav error("Keyboard-interactive authentication is disabled" 1120cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 1121cf2b5f3bSDag-Erling Smørgrav options.kbd_interactive_authentication = 0; 1122cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 1123d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1124cf2b5f3bSDag-Erling Smørgrav } 1125cf2b5f3bSDag-Erling Smørgrav if (options.challenge_response_authentication) { 1126cf2b5f3bSDag-Erling Smørgrav error("Challenge/response authentication is disabled" 1127cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 1128cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 1129d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1130cf2b5f3bSDag-Erling Smørgrav } 1131e8aafc91SKris Kennaway if (options.forward_agent) { 1132af12a3e7SDag-Erling Smørgrav error("Agent forwarding is disabled to avoid " 1133af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1134e8aafc91SKris Kennaway options.forward_agent = 0; 1135d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1136e8aafc91SKris Kennaway } 1137ca3176e7SBrian Feldman if (options.forward_x11) { 1138af12a3e7SDag-Erling Smørgrav error("X11 forwarding is disabled to avoid " 1139af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1140ca3176e7SBrian Feldman options.forward_x11 = 0; 1141d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1142ca3176e7SBrian Feldman } 1143af12a3e7SDag-Erling Smørgrav if (options.num_local_forwards > 0 || 1144af12a3e7SDag-Erling Smørgrav options.num_remote_forwards > 0) { 1145af12a3e7SDag-Erling Smørgrav error("Port forwarding is disabled to avoid " 1146af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1147af12a3e7SDag-Erling Smørgrav options.num_local_forwards = 1148af12a3e7SDag-Erling Smørgrav options.num_remote_forwards = 0; 1149d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1150ca3176e7SBrian Feldman } 1151333ee039SDag-Erling Smørgrav if (options.tun_open != SSH_TUNMODE_NO) { 1152333ee039SDag-Erling Smørgrav error("Tunnel forwarding is disabled to avoid " 1153333ee039SDag-Erling Smørgrav "man-in-the-middle attacks."); 1154333ee039SDag-Erling Smørgrav options.tun_open = SSH_TUNMODE_NO; 1155d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1156333ee039SDag-Erling Smørgrav } 1157d4af9e69SDag-Erling Smørgrav if (options.exit_on_forward_failure && cancelled_forwarding) 1158d4af9e69SDag-Erling Smørgrav fatal("Error: forwarding disabled due to host key " 1159d4af9e69SDag-Erling Smørgrav "check failure"); 1160d4af9e69SDag-Erling Smørgrav 1161e8aafc91SKris Kennaway /* 1162e8aafc91SKris Kennaway * XXX Should permit the user to change to use the new id. 1163e8aafc91SKris Kennaway * This could be done by converting the host key to an 1164e8aafc91SKris Kennaway * identifying sentence, tell that the host identifies itself 1165b15c8340SDag-Erling Smørgrav * by that sentence, and ask the user if he/she wishes to 1166e8aafc91SKris Kennaway * accept the authentication. 1167e8aafc91SKris Kennaway */ 1168e8aafc91SKris Kennaway break; 1169f388f5efSDag-Erling Smørgrav case HOST_FOUND: 1170f388f5efSDag-Erling Smørgrav fatal("internal error"); 1171f388f5efSDag-Erling Smørgrav break; 1172e8aafc91SKris Kennaway } 1173ca3176e7SBrian Feldman 1174ca3176e7SBrian Feldman if (options.check_host_ip && host_status != HOST_CHANGED && 1175ca3176e7SBrian Feldman ip_status == HOST_CHANGED) { 1176af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 1177af12a3e7SDag-Erling Smørgrav "Warning: the %s host key for '%.200s' " 1178af12a3e7SDag-Erling Smørgrav "differs from the key for the IP address '%.128s'" 11794a421b63SDag-Erling Smørgrav "\nOffending key for IP in %s:%lu", 11804a421b63SDag-Erling Smørgrav type, host, ip, ip_found->file, ip_found->line); 1181af12a3e7SDag-Erling Smørgrav if (host_status == HOST_OK) { 1182af12a3e7SDag-Erling Smørgrav len = strlen(msg); 1183af12a3e7SDag-Erling Smørgrav snprintf(msg + len, sizeof(msg) - len, 11844a421b63SDag-Erling Smørgrav "\nMatching host key in %s:%lu", 11854a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1186af12a3e7SDag-Erling Smørgrav } 1187ca3176e7SBrian Feldman if (options.strict_host_key_checking == 1) { 1188cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 1189af12a3e7SDag-Erling Smørgrav error("Exiting, you have requested strict checking."); 1190af12a3e7SDag-Erling Smørgrav goto fail; 1191ca3176e7SBrian Feldman } else if (options.strict_host_key_checking == 2) { 1192af12a3e7SDag-Erling Smørgrav strlcat(msg, "\nAre you sure you want " 1193af12a3e7SDag-Erling Smørgrav "to continue connecting (yes/no)? ", sizeof(msg)); 1194af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 1195af12a3e7SDag-Erling Smørgrav goto fail; 1196af12a3e7SDag-Erling Smørgrav } else { 1197cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 1198ca3176e7SBrian Feldman } 1199ca3176e7SBrian Feldman } 1200ca3176e7SBrian Feldman 1201bc5531deSDag-Erling Smørgrav if (!hostkey_trusted && options.update_hostkeys) { 1202bc5531deSDag-Erling Smørgrav debug("%s: hostkey not known or explicitly trusted: " 1203bc5531deSDag-Erling Smørgrav "disabling UpdateHostkeys", __func__); 1204bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 1205bc5531deSDag-Erling Smørgrav } 1206bc5531deSDag-Erling Smørgrav 1207e4a9863fSDag-Erling Smørgrav free(ip); 1208e4a9863fSDag-Erling Smørgrav free(host); 12094a421b63SDag-Erling Smørgrav if (host_hostkeys != NULL) 12104a421b63SDag-Erling Smørgrav free_hostkeys(host_hostkeys); 12114a421b63SDag-Erling Smørgrav if (ip_hostkeys != NULL) 12124a421b63SDag-Erling Smørgrav free_hostkeys(ip_hostkeys); 1213af12a3e7SDag-Erling Smørgrav return 0; 1214af12a3e7SDag-Erling Smørgrav 1215af12a3e7SDag-Erling Smørgrav fail: 1216b15c8340SDag-Erling Smørgrav if (want_cert && host_status != HOST_REVOKED) { 1217b15c8340SDag-Erling Smørgrav /* 1218b15c8340SDag-Erling Smørgrav * No matching certificate. Downgrade cert to raw key and 1219b15c8340SDag-Erling Smørgrav * search normally. 1220b15c8340SDag-Erling Smørgrav */ 1221b15c8340SDag-Erling Smørgrav debug("No matching CA found. Retry with plain key"); 1222b15c8340SDag-Erling Smørgrav raw_key = key_from_private(host_key); 1223b15c8340SDag-Erling Smørgrav if (key_drop_cert(raw_key) != 0) 1224b15c8340SDag-Erling Smørgrav fatal("Couldn't drop certificate"); 1225b15c8340SDag-Erling Smørgrav host_key = raw_key; 1226b15c8340SDag-Erling Smørgrav goto retry; 1227b15c8340SDag-Erling Smørgrav } 1228b15c8340SDag-Erling Smørgrav if (raw_key != NULL) 1229b15c8340SDag-Erling Smørgrav key_free(raw_key); 1230e4a9863fSDag-Erling Smørgrav free(ip); 1231e4a9863fSDag-Erling Smørgrav free(host); 12324a421b63SDag-Erling Smørgrav if (host_hostkeys != NULL) 12334a421b63SDag-Erling Smørgrav free_hostkeys(host_hostkeys); 12344a421b63SDag-Erling Smørgrav if (ip_hostkeys != NULL) 12354a421b63SDag-Erling Smørgrav free_hostkeys(ip_hostkeys); 1236af12a3e7SDag-Erling Smørgrav return -1; 1237e8aafc91SKris Kennaway } 1238511b41d2SMark Murray 1239cf2b5f3bSDag-Erling Smørgrav /* returns 0 if key verifies or -1 if key does NOT verify */ 1240fe5fd017SMark Murray int 1241af12a3e7SDag-Erling Smørgrav verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 1242fe5fd017SMark Murray { 1243*acc1a9efSDag-Erling Smørgrav u_int i; 1244a0ee8cc6SDag-Erling Smørgrav int r = -1, flags = 0; 1245*acc1a9efSDag-Erling Smørgrav char valid[64], *fp = NULL, *cafp = NULL; 1246bc5531deSDag-Erling Smørgrav struct sshkey *plain = NULL; 12474a421b63SDag-Erling Smørgrav 1248bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(host_key, 1249bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1250bc5531deSDag-Erling Smørgrav error("%s: fingerprint host key: %s", __func__, ssh_err(r)); 1251bc5531deSDag-Erling Smørgrav r = -1; 1252bc5531deSDag-Erling Smørgrav goto out; 1253bc5531deSDag-Erling Smørgrav } 1254fe5fd017SMark Murray 1255*acc1a9efSDag-Erling Smørgrav if (sshkey_is_cert(host_key)) { 1256*acc1a9efSDag-Erling Smørgrav if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, 1257*acc1a9efSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { 1258*acc1a9efSDag-Erling Smørgrav error("%s: fingerprint CA key: %s", 1259*acc1a9efSDag-Erling Smørgrav __func__, ssh_err(r)); 1260*acc1a9efSDag-Erling Smørgrav r = -1; 1261*acc1a9efSDag-Erling Smørgrav goto out; 1262*acc1a9efSDag-Erling Smørgrav } 1263*acc1a9efSDag-Erling Smørgrav sshkey_format_cert_validity(host_key->cert, 1264*acc1a9efSDag-Erling Smørgrav valid, sizeof(valid)); 1265*acc1a9efSDag-Erling Smørgrav debug("Server host certificate: %s %s, serial %llu " 1266*acc1a9efSDag-Erling Smørgrav "ID \"%s\" CA %s %s valid %s", 1267*acc1a9efSDag-Erling Smørgrav sshkey_ssh_name(host_key), fp, 1268*acc1a9efSDag-Erling Smørgrav (unsigned long long)host_key->cert->serial, 1269*acc1a9efSDag-Erling Smørgrav host_key->cert->key_id, 1270*acc1a9efSDag-Erling Smørgrav sshkey_ssh_name(host_key->cert->signature_key), cafp, 1271*acc1a9efSDag-Erling Smørgrav valid); 1272*acc1a9efSDag-Erling Smørgrav for (i = 0; i < host_key->cert->nprincipals; i++) { 1273*acc1a9efSDag-Erling Smørgrav debug2("Server host certificate hostname: %s", 1274*acc1a9efSDag-Erling Smørgrav host_key->cert->principals[i]); 1275*acc1a9efSDag-Erling Smørgrav } 1276*acc1a9efSDag-Erling Smørgrav } else { 1277*acc1a9efSDag-Erling Smørgrav debug("Server host key: %s %s", compat20 ? 1278*acc1a9efSDag-Erling Smørgrav sshkey_ssh_name(host_key) : sshkey_type(host_key), fp); 1279*acc1a9efSDag-Erling Smørgrav } 1280bc5531deSDag-Erling Smørgrav 1281bc5531deSDag-Erling Smørgrav if (sshkey_equal(previous_host_key, host_key)) { 1282bc5531deSDag-Erling Smørgrav debug2("%s: server host key %s %s matches cached key", 1283bc5531deSDag-Erling Smørgrav __func__, sshkey_type(host_key), fp); 1284bc5531deSDag-Erling Smørgrav r = 0; 1285bc5531deSDag-Erling Smørgrav goto out; 1286bc5531deSDag-Erling Smørgrav } 1287bc5531deSDag-Erling Smørgrav 1288bc5531deSDag-Erling Smørgrav /* Check in RevokedHostKeys file if specified */ 1289bc5531deSDag-Erling Smørgrav if (options.revoked_host_keys != NULL) { 1290bc5531deSDag-Erling Smørgrav r = sshkey_check_revoked(host_key, options.revoked_host_keys); 1291bc5531deSDag-Erling Smørgrav switch (r) { 1292bc5531deSDag-Erling Smørgrav case 0: 1293bc5531deSDag-Erling Smørgrav break; /* not revoked */ 1294bc5531deSDag-Erling Smørgrav case SSH_ERR_KEY_REVOKED: 1295bc5531deSDag-Erling Smørgrav error("Host key %s %s revoked by file %s", 1296bc5531deSDag-Erling Smørgrav sshkey_type(host_key), fp, 1297bc5531deSDag-Erling Smørgrav options.revoked_host_keys); 1298bc5531deSDag-Erling Smørgrav r = -1; 1299bc5531deSDag-Erling Smørgrav goto out; 1300bc5531deSDag-Erling Smørgrav default: 1301bc5531deSDag-Erling Smørgrav error("Error checking host key %s %s in " 1302bc5531deSDag-Erling Smørgrav "revoked keys file %s: %s", sshkey_type(host_key), 1303bc5531deSDag-Erling Smørgrav fp, options.revoked_host_keys, ssh_err(r)); 1304bc5531deSDag-Erling Smørgrav r = -1; 1305bc5531deSDag-Erling Smørgrav goto out; 1306bc5531deSDag-Erling Smørgrav } 1307a0ee8cc6SDag-Erling Smørgrav } 1308a0ee8cc6SDag-Erling Smørgrav 13093a0b9b77SXin LI if (options.verify_host_key_dns) { 13103a0b9b77SXin LI /* 13113a0b9b77SXin LI * XXX certs are not yet supported for DNS, so downgrade 13123a0b9b77SXin LI * them and try the plain key. 13133a0b9b77SXin LI */ 1314bc5531deSDag-Erling Smørgrav if ((r = sshkey_from_private(host_key, &plain)) != 0) 1315bc5531deSDag-Erling Smørgrav goto out; 1316bc5531deSDag-Erling Smørgrav if (sshkey_is_cert(plain)) 1317bc5531deSDag-Erling Smørgrav sshkey_drop_cert(plain); 13183a0b9b77SXin LI if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) { 13191ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_FOUND) { 13201ec0d754SDag-Erling Smørgrav if (options.verify_host_key_dns == 1 && 13211ec0d754SDag-Erling Smørgrav flags & DNS_VERIFY_MATCH && 13223a0b9b77SXin LI flags & DNS_VERIFY_SECURE) { 1323a0ee8cc6SDag-Erling Smørgrav r = 0; 1324bc5531deSDag-Erling Smørgrav goto out; 13253a0b9b77SXin LI } 13261ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_MATCH) { 13271ec0d754SDag-Erling Smørgrav matching_host_key_dns = 1; 13281ec0d754SDag-Erling Smørgrav } else { 13293a0b9b77SXin LI warn_changed_key(plain); 13303a0b9b77SXin LI error("Update the SSHFP RR in DNS " 13313a0b9b77SXin LI "with the new host key to get rid " 13323a0b9b77SXin LI "of this message."); 1333cf2b5f3bSDag-Erling Smørgrav } 1334cf2b5f3bSDag-Erling Smørgrav } 13351ec0d754SDag-Erling Smørgrav } 13363a0b9b77SXin LI } 1337a0ee8cc6SDag-Erling Smørgrav r = check_host_key(host, hostaddr, options.port, host_key, RDRW, 1338e146993eSDag-Erling Smørgrav options.user_hostfiles, options.num_user_hostfiles, 1339e146993eSDag-Erling Smørgrav options.system_hostfiles, options.num_system_hostfiles); 1340a0ee8cc6SDag-Erling Smørgrav 1341bc5531deSDag-Erling Smørgrav out: 1342bc5531deSDag-Erling Smørgrav sshkey_free(plain); 1343bc5531deSDag-Erling Smørgrav free(fp); 1344*acc1a9efSDag-Erling Smørgrav free(cafp); 1345a0ee8cc6SDag-Erling Smørgrav if (r == 0 && host_key != NULL) { 1346a0ee8cc6SDag-Erling Smørgrav key_free(previous_host_key); 1347a0ee8cc6SDag-Erling Smørgrav previous_host_key = key_from_private(host_key); 1348a0ee8cc6SDag-Erling Smørgrav } 1349a0ee8cc6SDag-Erling Smørgrav 1350a0ee8cc6SDag-Erling Smørgrav return r; 1351fe5fd017SMark Murray } 1352fe5fd017SMark Murray 1353511b41d2SMark Murray /* 1354511b41d2SMark Murray * Starts a dialog with the server, and authenticates the current user on the 1355511b41d2SMark Murray * server. This does not need any extra privileges. The basic connection 1356511b41d2SMark Murray * to the server must already have been established before this is called. 1357511b41d2SMark Murray * If login fails, this function prints an error and never returns. 1358511b41d2SMark Murray * This function does not require super-user privileges. 1359511b41d2SMark Murray */ 1360511b41d2SMark Murray void 136180628bacSDag-Erling Smørgrav ssh_login(Sensitive *sensitive, const char *orighost, 13624a421b63SDag-Erling Smørgrav struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) 1363511b41d2SMark Murray { 1364f7167e0eSDag-Erling Smørgrav char *host; 1365e8aafc91SKris Kennaway char *server_user, *local_user; 1366e8aafc91SKris Kennaway 1367e8aafc91SKris Kennaway local_user = xstrdup(pw->pw_name); 1368e8aafc91SKris Kennaway server_user = options.user ? options.user : local_user; 1369511b41d2SMark Murray 1370511b41d2SMark Murray /* Convert the user-supplied hostname into all lowercase. */ 1371511b41d2SMark Murray host = xstrdup(orighost); 1372f7167e0eSDag-Erling Smørgrav lowercase(host); 1373511b41d2SMark Murray 1374511b41d2SMark Murray /* Exchange protocol version identification strings with the server. */ 1375d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(timeout_ms); 1376511b41d2SMark Murray 1377511b41d2SMark Murray /* Put the connection into non-blocking mode. */ 1378511b41d2SMark Murray packet_set_nonblocking(); 1379511b41d2SMark Murray 1380511b41d2SMark Murray /* key exchange */ 1381511b41d2SMark Murray /* authenticate user */ 1382557f75e5SDag-Erling Smørgrav debug("Authenticating to %s:%d as '%s'", host, port, server_user); 1383e8aafc91SKris Kennaway if (compat20) { 13844a421b63SDag-Erling Smørgrav ssh_kex2(host, hostaddr, port); 138580628bacSDag-Erling Smørgrav ssh_userauth2(local_user, server_user, host, sensitive); 1386e8aafc91SKris Kennaway } else { 1387a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_SSH1 1388e8aafc91SKris Kennaway ssh_kex(host, hostaddr); 138980628bacSDag-Erling Smørgrav ssh_userauth1(local_user, server_user, host, sensitive); 1390a0ee8cc6SDag-Erling Smørgrav #else 1391557f75e5SDag-Erling Smørgrav fatal("ssh1 is not supported"); 1392a0ee8cc6SDag-Erling Smørgrav #endif 1393e8aafc91SKris Kennaway } 1394e4a9863fSDag-Erling Smørgrav free(local_user); 1395511b41d2SMark Murray } 1396e0fbb1d2SBrian Feldman 1397e0fbb1d2SBrian Feldman void 1398e0fbb1d2SBrian Feldman ssh_put_password(char *password) 1399e0fbb1d2SBrian Feldman { 1400e0fbb1d2SBrian Feldman int size; 1401e0fbb1d2SBrian Feldman char *padded; 1402e0fbb1d2SBrian Feldman 1403ca3176e7SBrian Feldman if (datafellows & SSH_BUG_PASSWORDPAD) { 1404af12a3e7SDag-Erling Smørgrav packet_put_cstring(password); 1405ca3176e7SBrian Feldman return; 1406ca3176e7SBrian Feldman } 1407e0fbb1d2SBrian Feldman size = roundup(strlen(password) + 1, 32); 1408333ee039SDag-Erling Smørgrav padded = xcalloc(1, size); 1409e0fbb1d2SBrian Feldman strlcpy(padded, password, size); 1410e0fbb1d2SBrian Feldman packet_put_string(padded, size); 1411b83788ffSDag-Erling Smørgrav explicit_bzero(padded, size); 1412e4a9863fSDag-Erling Smørgrav free(padded); 1413e0fbb1d2SBrian Feldman } 1414f388f5efSDag-Erling Smørgrav 1415f388f5efSDag-Erling Smørgrav /* print all known host keys for a given host, but skip keys of given type */ 1416f388f5efSDag-Erling Smørgrav static int 14174a421b63SDag-Erling Smørgrav show_other_keys(struct hostkeys *hostkeys, Key *key) 1418f388f5efSDag-Erling Smørgrav { 1419f7167e0eSDag-Erling Smørgrav int type[] = { 1420f7167e0eSDag-Erling Smørgrav KEY_RSA1, 1421f7167e0eSDag-Erling Smørgrav KEY_RSA, 1422f7167e0eSDag-Erling Smørgrav KEY_DSA, 1423f7167e0eSDag-Erling Smørgrav KEY_ECDSA, 1424f7167e0eSDag-Erling Smørgrav KEY_ED25519, 1425f7167e0eSDag-Erling Smørgrav -1 1426f7167e0eSDag-Erling Smørgrav }; 14274a421b63SDag-Erling Smørgrav int i, ret = 0; 14284a421b63SDag-Erling Smørgrav char *fp, *ra; 14294a421b63SDag-Erling Smørgrav const struct hostkey_entry *found; 1430f388f5efSDag-Erling Smørgrav 1431f388f5efSDag-Erling Smørgrav for (i = 0; type[i] != -1; i++) { 1432f388f5efSDag-Erling Smørgrav if (type[i] == key->type) 1433f388f5efSDag-Erling Smørgrav continue; 14344a421b63SDag-Erling Smørgrav if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) 1435f388f5efSDag-Erling Smørgrav continue; 1436bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(found->key, 1437bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT); 1438bc5531deSDag-Erling Smørgrav ra = sshkey_fingerprint(found->key, 1439bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_RANDOMART); 1440bc5531deSDag-Erling Smørgrav if (fp == NULL || ra == NULL) 1441bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint fail", __func__); 14424a421b63SDag-Erling Smørgrav logit("WARNING: %s key found for host %s\n" 14434a421b63SDag-Erling Smørgrav "in %s:%lu\n" 14444a421b63SDag-Erling Smørgrav "%s key fingerprint %s.", 14454a421b63SDag-Erling Smørgrav key_type(found->key), 14464a421b63SDag-Erling Smørgrav found->host, found->file, found->line, 14474a421b63SDag-Erling Smørgrav key_type(found->key), fp); 14484a421b63SDag-Erling Smørgrav if (options.visual_host_key) 14494a421b63SDag-Erling Smørgrav logit("%s", ra); 1450e4a9863fSDag-Erling Smørgrav free(ra); 1451e4a9863fSDag-Erling Smørgrav free(fp); 14524a421b63SDag-Erling Smørgrav ret = 1; 1453f388f5efSDag-Erling Smørgrav } 14544a421b63SDag-Erling Smørgrav return ret; 1455f388f5efSDag-Erling Smørgrav } 14561ec0d754SDag-Erling Smørgrav 14571ec0d754SDag-Erling Smørgrav static void 14581ec0d754SDag-Erling Smørgrav warn_changed_key(Key *host_key) 14591ec0d754SDag-Erling Smørgrav { 14601ec0d754SDag-Erling Smørgrav char *fp; 14611ec0d754SDag-Erling Smørgrav 1462bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(host_key, options.fingerprint_hash, 1463bc5531deSDag-Erling Smørgrav SSH_FP_DEFAULT); 1464bc5531deSDag-Erling Smørgrav if (fp == NULL) 1465bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint fail", __func__); 14661ec0d754SDag-Erling Smørgrav 14671ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 14681ec0d754SDag-Erling Smørgrav error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 14691ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 14701ec0d754SDag-Erling Smørgrav error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 14711ec0d754SDag-Erling Smørgrav error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 14724a421b63SDag-Erling Smørgrav error("It is also possible that a host key has just been changed."); 14731ec0d754SDag-Erling Smørgrav error("The fingerprint for the %s key sent by the remote host is\n%s.", 14744a421b63SDag-Erling Smørgrav key_type(host_key), fp); 14751ec0d754SDag-Erling Smørgrav error("Please contact your system administrator."); 14761ec0d754SDag-Erling Smørgrav 1477e4a9863fSDag-Erling Smørgrav free(fp); 14781ec0d754SDag-Erling Smørgrav } 1479b74df5b2SDag-Erling Smørgrav 1480b74df5b2SDag-Erling Smørgrav /* 1481b74df5b2SDag-Erling Smørgrav * Execute a local command 1482b74df5b2SDag-Erling Smørgrav */ 1483b74df5b2SDag-Erling Smørgrav int 1484b74df5b2SDag-Erling Smørgrav ssh_local_cmd(const char *args) 1485b74df5b2SDag-Erling Smørgrav { 1486b74df5b2SDag-Erling Smørgrav char *shell; 1487b74df5b2SDag-Erling Smørgrav pid_t pid; 1488b74df5b2SDag-Erling Smørgrav int status; 14894a421b63SDag-Erling Smørgrav void (*osighand)(int); 1490b74df5b2SDag-Erling Smørgrav 1491b74df5b2SDag-Erling Smørgrav if (!options.permit_local_command || 1492b74df5b2SDag-Erling Smørgrav args == NULL || !*args) 1493b74df5b2SDag-Erling Smørgrav return (1); 1494b74df5b2SDag-Erling Smørgrav 14954a421b63SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 1496b74df5b2SDag-Erling Smørgrav shell = _PATH_BSHELL; 1497b74df5b2SDag-Erling Smørgrav 14984a421b63SDag-Erling Smørgrav osighand = signal(SIGCHLD, SIG_DFL); 1499b74df5b2SDag-Erling Smørgrav pid = fork(); 1500b74df5b2SDag-Erling Smørgrav if (pid == 0) { 15014a421b63SDag-Erling Smørgrav signal(SIGPIPE, SIG_DFL); 1502b74df5b2SDag-Erling Smørgrav debug3("Executing %s -c \"%s\"", shell, args); 1503b74df5b2SDag-Erling Smørgrav execl(shell, shell, "-c", args, (char *)NULL); 1504b74df5b2SDag-Erling Smørgrav error("Couldn't execute %s -c \"%s\": %s", 1505b74df5b2SDag-Erling Smørgrav shell, args, strerror(errno)); 1506b74df5b2SDag-Erling Smørgrav _exit(1); 1507b74df5b2SDag-Erling Smørgrav } else if (pid == -1) 1508b74df5b2SDag-Erling Smørgrav fatal("fork failed: %.100s", strerror(errno)); 1509b74df5b2SDag-Erling Smørgrav while (waitpid(pid, &status, 0) == -1) 1510b74df5b2SDag-Erling Smørgrav if (errno != EINTR) 1511b74df5b2SDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 15124a421b63SDag-Erling Smørgrav signal(SIGCHLD, osighand); 1513b74df5b2SDag-Erling Smørgrav 1514b74df5b2SDag-Erling Smørgrav if (!WIFEXITED(status)) 1515b74df5b2SDag-Erling Smørgrav return (1); 1516b74df5b2SDag-Erling Smørgrav 1517b74df5b2SDag-Erling Smørgrav return (WEXITSTATUS(status)); 1518b74df5b2SDag-Erling Smørgrav } 1519*acc1a9efSDag-Erling Smørgrav 1520*acc1a9efSDag-Erling Smørgrav void 1521*acc1a9efSDag-Erling Smørgrav maybe_add_key_to_agent(char *authfile, Key *private, char *comment, 1522*acc1a9efSDag-Erling Smørgrav char *passphrase) 1523*acc1a9efSDag-Erling Smørgrav { 1524*acc1a9efSDag-Erling Smørgrav int auth_sock = -1, r; 1525*acc1a9efSDag-Erling Smørgrav 1526*acc1a9efSDag-Erling Smørgrav if (options.add_keys_to_agent == 0) 1527*acc1a9efSDag-Erling Smørgrav return; 1528*acc1a9efSDag-Erling Smørgrav 1529*acc1a9efSDag-Erling Smørgrav if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { 1530*acc1a9efSDag-Erling Smørgrav debug3("no authentication agent, not adding key"); 1531*acc1a9efSDag-Erling Smørgrav return; 1532*acc1a9efSDag-Erling Smørgrav } 1533*acc1a9efSDag-Erling Smørgrav 1534*acc1a9efSDag-Erling Smørgrav if (options.add_keys_to_agent == 2 && 1535*acc1a9efSDag-Erling Smørgrav !ask_permission("Add key %s (%s) to agent?", authfile, comment)) { 1536*acc1a9efSDag-Erling Smørgrav debug3("user denied adding this key"); 1537*acc1a9efSDag-Erling Smørgrav return; 1538*acc1a9efSDag-Erling Smørgrav } 1539*acc1a9efSDag-Erling Smørgrav 1540*acc1a9efSDag-Erling Smørgrav if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, 1541*acc1a9efSDag-Erling Smørgrav (options.add_keys_to_agent == 3))) == 0) 1542*acc1a9efSDag-Erling Smørgrav debug("identity added to agent: %s", authfile); 1543*acc1a9efSDag-Erling Smørgrav else 1544*acc1a9efSDag-Erling Smørgrav debug("could not add identity to agent: %s (%d)", authfile, r); 1545*acc1a9efSDag-Erling Smørgrav } 1546