1*b83788ffSDag-Erling Smørgrav /* $OpenBSD: sshconnect.c,v 1.246 2014/02/06 22:21:01 djm Exp $ */ 2e4a9863fSDag-Erling Smørgrav /* $FreeBSD$ */ 3511b41d2SMark Murray /* 4511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 5511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 6511b41d2SMark Murray * All rights reserved 7511b41d2SMark Murray * Code to connect to a remote host, and to perform the client side of the 8511b41d2SMark Murray * login (authentication) dialog. 942f71286SMark Murray * 10c2d3a559SKris Kennaway * As far as I am concerned, the code I have written for this software 11c2d3a559SKris Kennaway * can be used freely for any purpose. Any derived versions of this 12c2d3a559SKris Kennaway * software must be clearly marked as such, and if the derived work is 13c2d3a559SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 14c2d3a559SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 15511b41d2SMark Murray */ 16511b41d2SMark Murray 17511b41d2SMark Murray #include "includes.h" 18cf783db1SDag-Erling Smørgrav __RCSID("$FreeBSD$"); 19511b41d2SMark Murray 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" 60ca3176e7SBrian Feldman #include "readconf.h" 61ca3176e7SBrian Feldman #include "atomicio.h" 62ca3176e7SBrian Feldman #include "misc.h" 63cf2b5f3bSDag-Erling Smørgrav #include "dns.h" 647aee6ffeSDag-Erling Smørgrav #include "roaming.h" 65f7167e0eSDag-Erling Smørgrav #include "monitor_fdpass.h" 66b15c8340SDag-Erling Smørgrav #include "ssh2.h" 67333ee039SDag-Erling Smørgrav #include "version.h" 68cf2b5f3bSDag-Erling Smørgrav 69e8aafc91SKris Kennaway char *client_version_string = NULL; 70e8aafc91SKris Kennaway char *server_version_string = NULL; 71511b41d2SMark Murray 72b74df5b2SDag-Erling Smørgrav static int matching_host_key_dns = 0; 73cf2b5f3bSDag-Erling Smørgrav 744a421b63SDag-Erling Smørgrav static pid_t proxy_command_pid = 0; 754a421b63SDag-Erling Smørgrav 7680628bacSDag-Erling Smørgrav /* import */ 77511b41d2SMark Murray extern Options options; 78511b41d2SMark Murray extern char *__progname; 7980628bacSDag-Erling Smørgrav extern uid_t original_real_uid; 8080628bacSDag-Erling Smørgrav extern uid_t original_effective_uid; 81511b41d2SMark Murray 824a421b63SDag-Erling Smørgrav static int show_other_keys(struct hostkeys *, Key *); 831ec0d754SDag-Erling Smørgrav static void warn_changed_key(Key *); 84ca3176e7SBrian Feldman 85f7167e0eSDag-Erling Smørgrav /* Expand a proxy command */ 86f7167e0eSDag-Erling Smørgrav static char * 87f7167e0eSDag-Erling Smørgrav expand_proxy_command(const char *proxy_command, const char *user, 88f7167e0eSDag-Erling Smørgrav const char *host, int port) 89f7167e0eSDag-Erling Smørgrav { 90f7167e0eSDag-Erling Smørgrav char *tmp, *ret, strport[NI_MAXSERV]; 91f7167e0eSDag-Erling Smørgrav 92f7167e0eSDag-Erling Smørgrav snprintf(strport, sizeof strport, "%d", port); 93f7167e0eSDag-Erling Smørgrav xasprintf(&tmp, "exec %s", proxy_command); 94f7167e0eSDag-Erling Smørgrav ret = percent_expand(tmp, "h", host, "p", strport, 95f7167e0eSDag-Erling Smørgrav "r", options.user, (char *)NULL); 96f7167e0eSDag-Erling Smørgrav free(tmp); 97f7167e0eSDag-Erling Smørgrav return ret; 98f7167e0eSDag-Erling Smørgrav } 99f7167e0eSDag-Erling Smørgrav 100f7167e0eSDag-Erling Smørgrav /* 101f7167e0eSDag-Erling Smørgrav * Connect to the given ssh server using a proxy command that passes a 102f7167e0eSDag-Erling Smørgrav * a connected fd back to us. 103f7167e0eSDag-Erling Smørgrav */ 104f7167e0eSDag-Erling Smørgrav static int 105f7167e0eSDag-Erling Smørgrav ssh_proxy_fdpass_connect(const char *host, u_short port, 106f7167e0eSDag-Erling Smørgrav const char *proxy_command) 107f7167e0eSDag-Erling Smørgrav { 108f7167e0eSDag-Erling Smørgrav char *command_string; 109f7167e0eSDag-Erling Smørgrav int sp[2], sock; 110f7167e0eSDag-Erling Smørgrav pid_t pid; 111f7167e0eSDag-Erling Smørgrav char *shell; 112f7167e0eSDag-Erling Smørgrav 113f7167e0eSDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL) 114f7167e0eSDag-Erling Smørgrav shell = _PATH_BSHELL; 115f7167e0eSDag-Erling Smørgrav 116f7167e0eSDag-Erling Smørgrav if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) 117f7167e0eSDag-Erling Smørgrav fatal("Could not create socketpair to communicate with " 118f7167e0eSDag-Erling Smørgrav "proxy dialer: %.100s", strerror(errno)); 119f7167e0eSDag-Erling Smørgrav 120f7167e0eSDag-Erling Smørgrav command_string = expand_proxy_command(proxy_command, options.user, 121f7167e0eSDag-Erling Smørgrav host, port); 122f7167e0eSDag-Erling Smørgrav debug("Executing proxy dialer command: %.500s", command_string); 123f7167e0eSDag-Erling Smørgrav 124f7167e0eSDag-Erling Smørgrav /* Fork and execute the proxy command. */ 125f7167e0eSDag-Erling Smørgrav if ((pid = fork()) == 0) { 126f7167e0eSDag-Erling Smørgrav char *argv[10]; 127f7167e0eSDag-Erling Smørgrav 128f7167e0eSDag-Erling Smørgrav /* Child. Permanently give up superuser privileges. */ 129f7167e0eSDag-Erling Smørgrav permanently_drop_suid(original_real_uid); 130f7167e0eSDag-Erling Smørgrav 131f7167e0eSDag-Erling Smørgrav close(sp[1]); 132f7167e0eSDag-Erling Smørgrav /* Redirect stdin and stdout. */ 133f7167e0eSDag-Erling Smørgrav if (sp[0] != 0) { 134f7167e0eSDag-Erling Smørgrav if (dup2(sp[0], 0) < 0) 135f7167e0eSDag-Erling Smørgrav perror("dup2 stdin"); 136f7167e0eSDag-Erling Smørgrav } 137f7167e0eSDag-Erling Smørgrav if (sp[0] != 1) { 138f7167e0eSDag-Erling Smørgrav if (dup2(sp[0], 1) < 0) 139f7167e0eSDag-Erling Smørgrav perror("dup2 stdout"); 140f7167e0eSDag-Erling Smørgrav } 141f7167e0eSDag-Erling Smørgrav if (sp[0] >= 2) 142f7167e0eSDag-Erling Smørgrav close(sp[0]); 143f7167e0eSDag-Erling Smørgrav 144f7167e0eSDag-Erling Smørgrav /* 145f7167e0eSDag-Erling Smørgrav * Stderr is left as it is so that error messages get 146f7167e0eSDag-Erling Smørgrav * printed on the user's terminal. 147f7167e0eSDag-Erling Smørgrav */ 148f7167e0eSDag-Erling Smørgrav argv[0] = shell; 149f7167e0eSDag-Erling Smørgrav argv[1] = "-c"; 150f7167e0eSDag-Erling Smørgrav argv[2] = command_string; 151f7167e0eSDag-Erling Smørgrav argv[3] = NULL; 152f7167e0eSDag-Erling Smørgrav 153f7167e0eSDag-Erling Smørgrav /* 154f7167e0eSDag-Erling Smørgrav * Execute the proxy command. 155f7167e0eSDag-Erling Smørgrav * Note that we gave up any extra privileges above. 156f7167e0eSDag-Erling Smørgrav */ 157f7167e0eSDag-Erling Smørgrav execv(argv[0], argv); 158f7167e0eSDag-Erling Smørgrav perror(argv[0]); 159f7167e0eSDag-Erling Smørgrav exit(1); 160f7167e0eSDag-Erling Smørgrav } 161f7167e0eSDag-Erling Smørgrav /* Parent. */ 162f7167e0eSDag-Erling Smørgrav if (pid < 0) 163f7167e0eSDag-Erling Smørgrav fatal("fork failed: %.100s", strerror(errno)); 164f7167e0eSDag-Erling Smørgrav close(sp[0]); 165f7167e0eSDag-Erling Smørgrav free(command_string); 166f7167e0eSDag-Erling Smørgrav 167f7167e0eSDag-Erling Smørgrav if ((sock = mm_receive_fd(sp[1])) == -1) 168f7167e0eSDag-Erling Smørgrav fatal("proxy dialer did not pass back a connection"); 169f7167e0eSDag-Erling Smørgrav 170f7167e0eSDag-Erling Smørgrav while (waitpid(pid, NULL, 0) == -1) 171f7167e0eSDag-Erling Smørgrav if (errno != EINTR) 172f7167e0eSDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 173f7167e0eSDag-Erling Smørgrav 174f7167e0eSDag-Erling Smørgrav /* Set the connection file descriptors. */ 175f7167e0eSDag-Erling Smørgrav packet_set_connection(sock, sock); 176f7167e0eSDag-Erling Smørgrav 177f7167e0eSDag-Erling Smørgrav return 0; 178f7167e0eSDag-Erling Smørgrav } 179f7167e0eSDag-Erling Smørgrav 180511b41d2SMark Murray /* 181511b41d2SMark Murray * Connect to the given ssh server using a proxy command. 182511b41d2SMark Murray */ 183af12a3e7SDag-Erling Smørgrav static int 18480628bacSDag-Erling Smørgrav ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) 185511b41d2SMark Murray { 186f7167e0eSDag-Erling Smørgrav char *command_string; 187511b41d2SMark Murray int pin[2], pout[2]; 188e8aafc91SKris Kennaway pid_t pid; 189f7167e0eSDag-Erling Smørgrav char *shell; 190420bce64SDag-Erling Smørgrav 1914a421b63SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 192d4af9e69SDag-Erling Smørgrav shell = _PATH_BSHELL; 193511b41d2SMark Murray 194511b41d2SMark Murray /* Create pipes for communicating with the proxy. */ 195511b41d2SMark Murray if (pipe(pin) < 0 || pipe(pout) < 0) 196511b41d2SMark Murray fatal("Could not create pipes to communicate with the proxy: %.100s", 197511b41d2SMark Murray strerror(errno)); 198511b41d2SMark Murray 199f7167e0eSDag-Erling Smørgrav command_string = expand_proxy_command(proxy_command, options.user, 200f7167e0eSDag-Erling Smørgrav host, port); 201511b41d2SMark Murray debug("Executing proxy command: %.500s", command_string); 202511b41d2SMark Murray 203511b41d2SMark Murray /* Fork and execute the proxy command. */ 204511b41d2SMark Murray if ((pid = fork()) == 0) { 205511b41d2SMark Murray char *argv[10]; 206511b41d2SMark Murray 207511b41d2SMark Murray /* Child. Permanently give up superuser privileges. */ 208333ee039SDag-Erling Smørgrav permanently_drop_suid(original_real_uid); 209511b41d2SMark Murray 210511b41d2SMark Murray /* Redirect stdin and stdout. */ 211511b41d2SMark Murray close(pin[1]); 212511b41d2SMark Murray if (pin[0] != 0) { 213511b41d2SMark Murray if (dup2(pin[0], 0) < 0) 214511b41d2SMark Murray perror("dup2 stdin"); 215511b41d2SMark Murray close(pin[0]); 216511b41d2SMark Murray } 217511b41d2SMark Murray close(pout[0]); 218511b41d2SMark Murray if (dup2(pout[1], 1) < 0) 219511b41d2SMark Murray perror("dup2 stdout"); 220511b41d2SMark Murray /* Cannot be 1 because pin allocated two descriptors. */ 221511b41d2SMark Murray close(pout[1]); 222511b41d2SMark Murray 223511b41d2SMark Murray /* Stderr is left as it is so that error messages get 224511b41d2SMark Murray printed on the user's terminal. */ 225d4af9e69SDag-Erling Smørgrav argv[0] = shell; 226511b41d2SMark Murray argv[1] = "-c"; 227511b41d2SMark Murray argv[2] = command_string; 228511b41d2SMark Murray argv[3] = NULL; 229511b41d2SMark Murray 230511b41d2SMark Murray /* Execute the proxy command. Note that we gave up any 231511b41d2SMark Murray extra privileges above. */ 2324a421b63SDag-Erling Smørgrav signal(SIGPIPE, SIG_DFL); 233ca3176e7SBrian Feldman execv(argv[0], argv); 234ca3176e7SBrian Feldman perror(argv[0]); 235511b41d2SMark Murray exit(1); 236511b41d2SMark Murray } 237511b41d2SMark Murray /* Parent. */ 238511b41d2SMark Murray if (pid < 0) 239511b41d2SMark Murray fatal("fork failed: %.100s", strerror(errno)); 240f388f5efSDag-Erling Smørgrav else 241f388f5efSDag-Erling Smørgrav proxy_command_pid = pid; /* save pid to clean up later */ 242511b41d2SMark Murray 243511b41d2SMark Murray /* Close child side of the descriptors. */ 244511b41d2SMark Murray close(pin[0]); 245511b41d2SMark Murray close(pout[1]); 246511b41d2SMark Murray 247511b41d2SMark Murray /* Free the command name. */ 248e4a9863fSDag-Erling Smørgrav free(command_string); 249511b41d2SMark Murray 250511b41d2SMark Murray /* Set the connection file descriptors. */ 251511b41d2SMark Murray packet_set_connection(pout[0], pin[1]); 252511b41d2SMark Murray 253af12a3e7SDag-Erling Smørgrav /* Indicate OK return */ 254af12a3e7SDag-Erling Smørgrav return 0; 255511b41d2SMark Murray } 256511b41d2SMark Murray 2574a421b63SDag-Erling Smørgrav void 2584a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(void) 2594a421b63SDag-Erling Smørgrav { 2604a421b63SDag-Erling Smørgrav /* 2614a421b63SDag-Erling Smørgrav * Send SIGHUP to proxy command if used. We don't wait() in 2624a421b63SDag-Erling Smørgrav * case it hangs and instead rely on init to reap the child 2634a421b63SDag-Erling Smørgrav */ 2644a421b63SDag-Erling Smørgrav if (proxy_command_pid > 1) 2654a421b63SDag-Erling Smørgrav kill(proxy_command_pid, SIGHUP); 2664a421b63SDag-Erling Smørgrav } 2674a421b63SDag-Erling Smørgrav 268511b41d2SMark Murray /* 26989986192SBrooks Davis * Set TCP receive buffer if requested. 27089986192SBrooks Davis * Note: tuning needs to happen after the socket is created but before the 27189986192SBrooks Davis * connection happens so winscale is negotiated properly. 27289986192SBrooks Davis */ 27389986192SBrooks Davis static void 27489986192SBrooks Davis ssh_set_socket_recvbuf(int sock) 27589986192SBrooks Davis { 27689986192SBrooks Davis void *buf = (void *)&options.tcp_rcv_buf; 27789986192SBrooks Davis int socksize, sz = sizeof(options.tcp_rcv_buf); 27889986192SBrooks Davis socklen_t len = sizeof(int); 27989986192SBrooks Davis 28089986192SBrooks Davis debug("setsockopt attempting to set SO_RCVBUF to %d", 28189986192SBrooks Davis options.tcp_rcv_buf); 28289986192SBrooks Davis if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, buf, sz) >= 0) { 28389986192SBrooks Davis getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &socksize, &len); 28489986192SBrooks Davis debug("setsockopt SO_RCVBUF: %.100s %d", strerror(errno), 28589986192SBrooks Davis socksize); 28689986192SBrooks Davis } else 28789986192SBrooks Davis error("Couldn't set socket receive buffer to %d: %.100s", 28889986192SBrooks Davis options.tcp_rcv_buf, strerror(errno)); 28989986192SBrooks Davis } 29089986192SBrooks Davis 29189986192SBrooks Davis /* 292511b41d2SMark Murray * Creates a (possibly privileged) socket for use as the ssh connection. 293511b41d2SMark Murray */ 294af12a3e7SDag-Erling Smørgrav static int 295cf2b5f3bSDag-Erling Smørgrav ssh_create_socket(int privileged, struct addrinfo *ai) 296511b41d2SMark Murray { 297f7167e0eSDag-Erling Smørgrav int sock, r, gaierr; 298*b83788ffSDag-Erling Smørgrav struct addrinfo hints, *res = NULL; 299511b41d2SMark Murray 300cf2b5f3bSDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 301b15c8340SDag-Erling Smørgrav if (sock < 0) { 302f7167e0eSDag-Erling Smørgrav error("socket: %s", strerror(errno)); 303b15c8340SDag-Erling Smørgrav return -1; 304b15c8340SDag-Erling Smørgrav } 305b15c8340SDag-Erling Smørgrav fcntl(sock, F_SETFD, FD_CLOEXEC); 306af12a3e7SDag-Erling Smørgrav 30789986192SBrooks Davis if (options.tcp_rcv_buf > 0) 30889986192SBrooks Davis ssh_set_socket_recvbuf(sock); 30989986192SBrooks Davis 310af12a3e7SDag-Erling Smørgrav /* Bind the socket to an alternative local IP address */ 311f7167e0eSDag-Erling Smørgrav if (options.bind_address == NULL && !privileged) 312af12a3e7SDag-Erling Smørgrav return sock; 313af12a3e7SDag-Erling Smørgrav 314*b83788ffSDag-Erling Smørgrav if (options.bind_address) { 315af12a3e7SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 316cf2b5f3bSDag-Erling Smørgrav hints.ai_family = ai->ai_family; 317cf2b5f3bSDag-Erling Smørgrav hints.ai_socktype = ai->ai_socktype; 318cf2b5f3bSDag-Erling Smørgrav hints.ai_protocol = ai->ai_protocol; 319af12a3e7SDag-Erling Smørgrav hints.ai_flags = AI_PASSIVE; 320d4af9e69SDag-Erling Smørgrav gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); 321af12a3e7SDag-Erling Smørgrav if (gaierr) { 322af12a3e7SDag-Erling Smørgrav error("getaddrinfo: %s: %s", options.bind_address, 323d4af9e69SDag-Erling Smørgrav ssh_gai_strerror(gaierr)); 324af12a3e7SDag-Erling Smørgrav close(sock); 325af12a3e7SDag-Erling Smørgrav return -1; 326511b41d2SMark Murray } 327*b83788ffSDag-Erling Smørgrav } 328f7167e0eSDag-Erling Smørgrav /* 329f7167e0eSDag-Erling Smørgrav * If we are running as root and want to connect to a privileged 330f7167e0eSDag-Erling Smørgrav * port, bind our own socket to a privileged port. 331f7167e0eSDag-Erling Smørgrav */ 332f7167e0eSDag-Erling Smørgrav if (privileged) { 333f7167e0eSDag-Erling Smørgrav PRIV_START; 334*b83788ffSDag-Erling Smørgrav r = bindresvport_sa(sock, res ? res->ai_addr : NULL); 335f7167e0eSDag-Erling Smørgrav PRIV_END; 336f7167e0eSDag-Erling Smørgrav if (r < 0) { 337f7167e0eSDag-Erling Smørgrav error("bindresvport_sa: af=%d %s", ai->ai_family, 338f7167e0eSDag-Erling Smørgrav strerror(errno)); 339f7167e0eSDag-Erling Smørgrav goto fail; 340f7167e0eSDag-Erling Smørgrav } 341f7167e0eSDag-Erling Smørgrav } else { 342af12a3e7SDag-Erling Smørgrav if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 343f7167e0eSDag-Erling Smørgrav error("bind: %s: %s", options.bind_address, 344f7167e0eSDag-Erling Smørgrav strerror(errno)); 345f7167e0eSDag-Erling Smørgrav fail: 346af12a3e7SDag-Erling Smørgrav close(sock); 347af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 348af12a3e7SDag-Erling Smørgrav return -1; 349af12a3e7SDag-Erling Smørgrav } 350f7167e0eSDag-Erling Smørgrav } 351*b83788ffSDag-Erling Smørgrav if (res != NULL) 352af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 353511b41d2SMark Murray return sock; 354511b41d2SMark Murray } 355511b41d2SMark Murray 356cf2b5f3bSDag-Erling Smørgrav static int 357cf2b5f3bSDag-Erling Smørgrav timeout_connect(int sockfd, const struct sockaddr *serv_addr, 358d4af9e69SDag-Erling Smørgrav socklen_t addrlen, int *timeoutp) 359cf2b5f3bSDag-Erling Smørgrav { 360cf2b5f3bSDag-Erling Smørgrav fd_set *fdset; 361d4af9e69SDag-Erling Smørgrav struct timeval tv, t_start; 362cf2b5f3bSDag-Erling Smørgrav socklen_t optlen; 363333ee039SDag-Erling Smørgrav int optval, rc, result = -1; 364cf2b5f3bSDag-Erling Smørgrav 365d4af9e69SDag-Erling Smørgrav gettimeofday(&t_start, NULL); 366d4af9e69SDag-Erling Smørgrav 367d4af9e69SDag-Erling Smørgrav if (*timeoutp <= 0) { 368d4af9e69SDag-Erling Smørgrav result = connect(sockfd, serv_addr, addrlen); 369d4af9e69SDag-Erling Smørgrav goto done; 370d4af9e69SDag-Erling Smørgrav } 371cf2b5f3bSDag-Erling Smørgrav 3721ec0d754SDag-Erling Smørgrav set_nonblock(sockfd); 373cf2b5f3bSDag-Erling Smørgrav rc = connect(sockfd, serv_addr, addrlen); 3741ec0d754SDag-Erling Smørgrav if (rc == 0) { 3751ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 376d4af9e69SDag-Erling Smørgrav result = 0; 377d4af9e69SDag-Erling Smørgrav goto done; 3781ec0d754SDag-Erling Smørgrav } 379d4af9e69SDag-Erling Smørgrav if (errno != EINPROGRESS) { 380d4af9e69SDag-Erling Smørgrav result = -1; 381d4af9e69SDag-Erling Smørgrav goto done; 382d4af9e69SDag-Erling Smørgrav } 383cf2b5f3bSDag-Erling Smørgrav 384333ee039SDag-Erling Smørgrav fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), 385333ee039SDag-Erling Smørgrav sizeof(fd_mask)); 386cf2b5f3bSDag-Erling Smørgrav FD_SET(sockfd, fdset); 387d4af9e69SDag-Erling Smørgrav ms_to_timeval(&tv, *timeoutp); 388cf2b5f3bSDag-Erling Smørgrav 389cf2b5f3bSDag-Erling Smørgrav for (;;) { 390cf2b5f3bSDag-Erling Smørgrav rc = select(sockfd + 1, NULL, fdset, NULL, &tv); 391cf2b5f3bSDag-Erling Smørgrav if (rc != -1 || errno != EINTR) 392cf2b5f3bSDag-Erling Smørgrav break; 393cf2b5f3bSDag-Erling Smørgrav } 394cf2b5f3bSDag-Erling Smørgrav 395cf2b5f3bSDag-Erling Smørgrav switch (rc) { 396cf2b5f3bSDag-Erling Smørgrav case 0: 397cf2b5f3bSDag-Erling Smørgrav /* Timed out */ 398cf2b5f3bSDag-Erling Smørgrav errno = ETIMEDOUT; 399cf2b5f3bSDag-Erling Smørgrav break; 400cf2b5f3bSDag-Erling Smørgrav case -1: 401cf2b5f3bSDag-Erling Smørgrav /* Select error */ 402cf2b5f3bSDag-Erling Smørgrav debug("select: %s", strerror(errno)); 403cf2b5f3bSDag-Erling Smørgrav break; 404cf2b5f3bSDag-Erling Smørgrav case 1: 405cf2b5f3bSDag-Erling Smørgrav /* Completed or failed */ 406cf2b5f3bSDag-Erling Smørgrav optval = 0; 407cf2b5f3bSDag-Erling Smørgrav optlen = sizeof(optval); 408cf2b5f3bSDag-Erling Smørgrav if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, 409cf2b5f3bSDag-Erling Smørgrav &optlen) == -1) { 410cf2b5f3bSDag-Erling Smørgrav debug("getsockopt: %s", strerror(errno)); 411cf2b5f3bSDag-Erling Smørgrav break; 412cf2b5f3bSDag-Erling Smørgrav } 413cf2b5f3bSDag-Erling Smørgrav if (optval != 0) { 414cf2b5f3bSDag-Erling Smørgrav errno = optval; 415cf2b5f3bSDag-Erling Smørgrav break; 416cf2b5f3bSDag-Erling Smørgrav } 417cf2b5f3bSDag-Erling Smørgrav result = 0; 4181ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 419cf2b5f3bSDag-Erling Smørgrav break; 420cf2b5f3bSDag-Erling Smørgrav default: 421cf2b5f3bSDag-Erling Smørgrav /* Should not occur */ 422cf2b5f3bSDag-Erling Smørgrav fatal("Bogus return (%d) from select()", rc); 423cf2b5f3bSDag-Erling Smørgrav } 424cf2b5f3bSDag-Erling Smørgrav 425e4a9863fSDag-Erling Smørgrav free(fdset); 426d4af9e69SDag-Erling Smørgrav 427d4af9e69SDag-Erling Smørgrav done: 428d4af9e69SDag-Erling Smørgrav if (result == 0 && *timeoutp > 0) { 429d4af9e69SDag-Erling Smørgrav ms_subtract_diff(&t_start, timeoutp); 430d4af9e69SDag-Erling Smørgrav if (*timeoutp <= 0) { 431d4af9e69SDag-Erling Smørgrav errno = ETIMEDOUT; 432d4af9e69SDag-Erling Smørgrav result = -1; 433d4af9e69SDag-Erling Smørgrav } 434d4af9e69SDag-Erling Smørgrav } 435d4af9e69SDag-Erling Smørgrav 436cf2b5f3bSDag-Erling Smørgrav return (result); 437cf2b5f3bSDag-Erling Smørgrav } 438cf2b5f3bSDag-Erling Smørgrav 439511b41d2SMark Murray /* 440511b41d2SMark Murray * Opens a TCP/IP connection to the remote server on the given host. 441511b41d2SMark Murray * The address of the remote host will be returned in hostaddr. 44280628bacSDag-Erling Smørgrav * If port is 0, the default port will be used. If needpriv is true, 443511b41d2SMark Murray * a privileged port will be allocated to make the connection. 44480628bacSDag-Erling Smørgrav * This requires super-user privileges if needpriv is true. 445511b41d2SMark Murray * Connection_attempts specifies the maximum number of tries (one per 446511b41d2SMark Murray * second). If proxy_command is non-NULL, it specifies the command (with %h 447511b41d2SMark Murray * and %p substituted for host and port, respectively) to use to contact 448511b41d2SMark Murray * the daemon. 449511b41d2SMark Murray */ 450f7167e0eSDag-Erling Smørgrav static int 451f7167e0eSDag-Erling Smørgrav ssh_connect_direct(const char *host, struct addrinfo *aitop, 452f7167e0eSDag-Erling Smørgrav struct sockaddr_storage *hostaddr, u_short port, int family, 453f7167e0eSDag-Erling Smørgrav int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) 454511b41d2SMark Murray { 455ca3176e7SBrian Feldman int on = 1; 456ca3176e7SBrian Feldman int sock = -1, attempt; 457ca3176e7SBrian Feldman char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 458f7167e0eSDag-Erling Smørgrav struct addrinfo *ai; 459511b41d2SMark Murray 460e73e9afaSDag-Erling Smørgrav debug2("ssh_connect: needpriv %d", needpriv); 461511b41d2SMark Murray 462333ee039SDag-Erling Smørgrav for (attempt = 0; attempt < connection_attempts; attempt++) { 46362efe23aSDag-Erling Smørgrav if (attempt > 0) { 46462efe23aSDag-Erling Smørgrav /* Sleep a moment before retrying. */ 46562efe23aSDag-Erling Smørgrav sleep(1); 466511b41d2SMark Murray debug("Trying again..."); 46762efe23aSDag-Erling Smørgrav } 468333ee039SDag-Erling Smørgrav /* 469333ee039SDag-Erling Smørgrav * Loop through addresses for this host, and try each one in 470333ee039SDag-Erling Smørgrav * sequence until the connection succeeds. 471333ee039SDag-Erling Smørgrav */ 472511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 473f7167e0eSDag-Erling Smørgrav if (ai->ai_family != AF_INET && 474f7167e0eSDag-Erling Smørgrav ai->ai_family != AF_INET6) 475511b41d2SMark Murray continue; 476511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 477511b41d2SMark Murray ntop, sizeof(ntop), strport, sizeof(strport), 478511b41d2SMark Murray NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 479511b41d2SMark Murray error("ssh_connect: getnameinfo failed"); 480511b41d2SMark Murray continue; 481511b41d2SMark Murray } 482511b41d2SMark Murray debug("Connecting to %.200s [%.100s] port %s.", 4831f5ce8f4SBrian Feldman host, ntop, strport); 484511b41d2SMark Murray 485511b41d2SMark Murray /* Create a socket for connecting. */ 486cf2b5f3bSDag-Erling Smørgrav sock = ssh_create_socket(needpriv, ai); 487511b41d2SMark Murray if (sock < 0) 488af12a3e7SDag-Erling Smørgrav /* Any error is already output */ 489511b41d2SMark Murray continue; 490511b41d2SMark Murray 491cf2b5f3bSDag-Erling Smørgrav if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 492d4af9e69SDag-Erling Smørgrav timeout_ms) >= 0) { 493511b41d2SMark Murray /* Successful connection. */ 494c322fe35SKris Kennaway memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 495511b41d2SMark Murray break; 496511b41d2SMark Murray } else { 497f388f5efSDag-Erling Smørgrav debug("connect to address %s port %s: %s", 498f388f5efSDag-Erling Smørgrav ntop, strport, strerror(errno)); 499511b41d2SMark Murray close(sock); 500333ee039SDag-Erling Smørgrav sock = -1; 501511b41d2SMark Murray } 502511b41d2SMark Murray } 503333ee039SDag-Erling Smørgrav if (sock != -1) 504511b41d2SMark Murray break; /* Successful connection. */ 505511b41d2SMark Murray } 506511b41d2SMark Murray 507511b41d2SMark Murray /* Return failure if we didn't get a successful connection. */ 508333ee039SDag-Erling Smørgrav if (sock == -1) { 509aa49c926SDag-Erling Smørgrav error("ssh: connect to host %s port %s: %s", 510f388f5efSDag-Erling Smørgrav host, strport, strerror(errno)); 511aa49c926SDag-Erling Smørgrav return (-1); 512f388f5efSDag-Erling Smørgrav } 513511b41d2SMark Murray 514511b41d2SMark Murray debug("Connection established."); 515511b41d2SMark Murray 5161ec0d754SDag-Erling Smørgrav /* Set SO_KEEPALIVE if requested. */ 517d4af9e69SDag-Erling Smørgrav if (want_keepalive && 518ca3176e7SBrian Feldman setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 519ca3176e7SBrian Feldman sizeof(on)) < 0) 520ca3176e7SBrian Feldman error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 521ca3176e7SBrian Feldman 522511b41d2SMark Murray /* Set the connection. */ 523511b41d2SMark Murray packet_set_connection(sock, sock); 524511b41d2SMark Murray 525af12a3e7SDag-Erling Smørgrav return 0; 526511b41d2SMark Murray } 527511b41d2SMark Murray 528f7167e0eSDag-Erling Smørgrav int 529f7167e0eSDag-Erling Smørgrav ssh_connect(const char *host, struct addrinfo *addrs, 530f7167e0eSDag-Erling Smørgrav struct sockaddr_storage *hostaddr, u_short port, int family, 531f7167e0eSDag-Erling Smørgrav int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) 532f7167e0eSDag-Erling Smørgrav { 533f7167e0eSDag-Erling Smørgrav if (options.proxy_command == NULL) { 534f7167e0eSDag-Erling Smørgrav return ssh_connect_direct(host, addrs, hostaddr, port, family, 535f7167e0eSDag-Erling Smørgrav connection_attempts, timeout_ms, want_keepalive, needpriv); 536f7167e0eSDag-Erling Smørgrav } else if (strcmp(options.proxy_command, "-") == 0) { 537f7167e0eSDag-Erling Smørgrav packet_set_connection(STDIN_FILENO, STDOUT_FILENO); 538f7167e0eSDag-Erling Smørgrav return 0; /* Always succeeds */ 539f7167e0eSDag-Erling Smørgrav } else if (options.proxy_use_fdpass) { 540f7167e0eSDag-Erling Smørgrav return ssh_proxy_fdpass_connect(host, port, 541f7167e0eSDag-Erling Smørgrav options.proxy_command); 542f7167e0eSDag-Erling Smørgrav } 543f7167e0eSDag-Erling Smørgrav return ssh_proxy_connect(host, port, options.proxy_command); 544f7167e0eSDag-Erling Smørgrav } 545f7167e0eSDag-Erling Smørgrav 5466888a9beSDag-Erling Smørgrav static void 5476888a9beSDag-Erling Smørgrav send_client_banner(int connection_out, int minor1) 5486888a9beSDag-Erling Smørgrav { 5496888a9beSDag-Erling Smørgrav /* Send our own protocol version identification. */ 5506888a9beSDag-Erling Smørgrav xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s%s%s", 5516888a9beSDag-Erling Smørgrav compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 5526888a9beSDag-Erling Smørgrav compat20 ? PROTOCOL_MINOR_2 : minor1, 5536888a9beSDag-Erling Smørgrav SSH_VERSION, options.hpn_disabled ? "" : SSH_VERSION_HPN, 5546888a9beSDag-Erling Smørgrav *options.version_addendum == '\0' ? "" : " ", 5556888a9beSDag-Erling Smørgrav options.version_addendum, compat20 ? "\r\n" : "\n"); 5566888a9beSDag-Erling Smørgrav if (roaming_atomicio(vwrite, connection_out, client_version_string, 5576888a9beSDag-Erling Smørgrav strlen(client_version_string)) != strlen(client_version_string)) 5586888a9beSDag-Erling Smørgrav fatal("write: %.100s", strerror(errno)); 5596888a9beSDag-Erling Smørgrav chop(client_version_string); 5606888a9beSDag-Erling Smørgrav debug("Local version string %.100s", client_version_string); 5616888a9beSDag-Erling Smørgrav } 5626888a9beSDag-Erling Smørgrav 563511b41d2SMark Murray /* 564e8aafc91SKris Kennaway * Waits for the server identification string, and sends our own 565e8aafc91SKris Kennaway * identification string. 566511b41d2SMark Murray */ 5677aee6ffeSDag-Erling Smørgrav void 568d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(int timeout_ms) 569511b41d2SMark Murray { 570e8aafc91SKris Kennaway char buf[256], remote_version[256]; /* must be same size! */ 571d4ecd108SDag-Erling Smørgrav int remote_major, remote_minor, mismatch; 572e8aafc91SKris Kennaway int connection_in = packet_get_connection_in(); 573e8aafc91SKris Kennaway int connection_out = packet_get_connection_out(); 5746888a9beSDag-Erling Smørgrav int minor1 = PROTOCOL_MINOR_1, client_banner_sent = 0; 575333ee039SDag-Erling Smørgrav u_int i, n; 576d4af9e69SDag-Erling Smørgrav size_t len; 577d4af9e69SDag-Erling Smørgrav int fdsetsz, remaining, rc; 578d4af9e69SDag-Erling Smørgrav struct timeval t_start, t_remaining; 579d4af9e69SDag-Erling Smørgrav fd_set *fdset; 580d4af9e69SDag-Erling Smørgrav 581d4af9e69SDag-Erling Smørgrav fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); 582d4af9e69SDag-Erling Smørgrav fdset = xcalloc(1, fdsetsz); 583511b41d2SMark Murray 5846888a9beSDag-Erling Smørgrav /* 5856888a9beSDag-Erling Smørgrav * If we are SSH2-only then we can send the banner immediately and 5866888a9beSDag-Erling Smørgrav * save a round-trip. 5876888a9beSDag-Erling Smørgrav */ 5886888a9beSDag-Erling Smørgrav if (options.protocol == SSH_PROTO_2) { 5896888a9beSDag-Erling Smørgrav enable_compat20(); 5906888a9beSDag-Erling Smørgrav send_client_banner(connection_out, 0); 5916888a9beSDag-Erling Smørgrav client_banner_sent = 1; 5926888a9beSDag-Erling Smørgrav } 5936888a9beSDag-Erling Smørgrav 594d4ecd108SDag-Erling Smørgrav /* Read other side's version identification. */ 595d4af9e69SDag-Erling Smørgrav remaining = timeout_ms; 596333ee039SDag-Erling Smørgrav for (n = 0;;) { 597e8aafc91SKris Kennaway for (i = 0; i < sizeof(buf) - 1; i++) { 598d4af9e69SDag-Erling Smørgrav if (timeout_ms > 0) { 599d4af9e69SDag-Erling Smørgrav gettimeofday(&t_start, NULL); 600d4af9e69SDag-Erling Smørgrav ms_to_timeval(&t_remaining, remaining); 601d4af9e69SDag-Erling Smørgrav FD_SET(connection_in, fdset); 602d4af9e69SDag-Erling Smørgrav rc = select(connection_in + 1, fdset, NULL, 603d4af9e69SDag-Erling Smørgrav fdset, &t_remaining); 604d4af9e69SDag-Erling Smørgrav ms_subtract_diff(&t_start, &remaining); 605d4af9e69SDag-Erling Smørgrav if (rc == 0 || remaining <= 0) 606d4af9e69SDag-Erling Smørgrav fatal("Connection timed out during " 607d4af9e69SDag-Erling Smørgrav "banner exchange"); 608d4af9e69SDag-Erling Smørgrav if (rc == -1) { 609d4af9e69SDag-Erling Smørgrav if (errno == EINTR) 610d4af9e69SDag-Erling Smørgrav continue; 611d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 612d4af9e69SDag-Erling Smørgrav "select: %s", strerror(errno)); 613d4af9e69SDag-Erling Smørgrav } 614d4af9e69SDag-Erling Smørgrav } 615d4af9e69SDag-Erling Smørgrav 6167aee6ffeSDag-Erling Smørgrav len = roaming_atomicio(read, connection_in, &buf[i], 1); 617d4ecd108SDag-Erling Smørgrav 618d4ecd108SDag-Erling Smørgrav if (len != 1 && errno == EPIPE) 619d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 620d4af9e69SDag-Erling Smørgrav "Connection closed by remote host"); 621d4ecd108SDag-Erling Smørgrav else if (len != 1) 622d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 623d4af9e69SDag-Erling Smørgrav "read: %.100s", strerror(errno)); 624e8aafc91SKris Kennaway if (buf[i] == '\r') { 625e8aafc91SKris Kennaway buf[i] = '\n'; 626e8aafc91SKris Kennaway buf[i + 1] = 0; 627e8aafc91SKris Kennaway continue; /**XXX wait for \n */ 628511b41d2SMark Murray } 629e8aafc91SKris Kennaway if (buf[i] == '\n') { 630e8aafc91SKris Kennaway buf[i + 1] = 0; 631511b41d2SMark Murray break; 632e8aafc91SKris Kennaway } 633333ee039SDag-Erling Smørgrav if (++n > 65536) 634d4af9e69SDag-Erling Smørgrav fatal("ssh_exchange_identification: " 635d4af9e69SDag-Erling Smørgrav "No banner received"); 636e8aafc91SKris Kennaway } 637e8aafc91SKris Kennaway buf[sizeof(buf) - 1] = 0; 638c2d3a559SKris Kennaway if (strncmp(buf, "SSH-", 4) == 0) 639c2d3a559SKris Kennaway break; 640c2d3a559SKris Kennaway debug("ssh_exchange_identification: %s", buf); 641c2d3a559SKris Kennaway } 642e8aafc91SKris Kennaway server_version_string = xstrdup(buf); 643e4a9863fSDag-Erling Smørgrav free(fdset); 644511b41d2SMark Murray 645511b41d2SMark Murray /* 646e8aafc91SKris Kennaway * Check that the versions match. In future this might accept 647e8aafc91SKris Kennaway * several versions and set appropriate flags to handle them. 648511b41d2SMark Murray */ 649e8aafc91SKris Kennaway if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 650e8aafc91SKris Kennaway &remote_major, &remote_minor, remote_version) != 3) 651e8aafc91SKris Kennaway fatal("Bad remote protocol version identification: '%.100s'", buf); 652e8aafc91SKris Kennaway debug("Remote protocol version %d.%d, remote software version %.100s", 653e8aafc91SKris Kennaway remote_major, remote_minor, remote_version); 654511b41d2SMark Murray 655e8aafc91SKris Kennaway compat_datafellows(remote_version); 656e8aafc91SKris Kennaway mismatch = 0; 657e8aafc91SKris Kennaway 658e8aafc91SKris Kennaway switch (remote_major) { 659e8aafc91SKris Kennaway case 1: 660e8aafc91SKris Kennaway if (remote_minor == 99 && 661e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) && 662e8aafc91SKris Kennaway !(options.protocol & SSH_PROTO_1_PREFERRED)) { 663e8aafc91SKris Kennaway enable_compat20(); 664511b41d2SMark Murray break; 665e8aafc91SKris Kennaway } 666e8aafc91SKris Kennaway if (!(options.protocol & SSH_PROTO_1)) { 667e8aafc91SKris Kennaway mismatch = 1; 668e8aafc91SKris Kennaway break; 669e8aafc91SKris Kennaway } 670e8aafc91SKris Kennaway if (remote_minor < 3) { 671e8aafc91SKris Kennaway fatal("Remote machine has too old SSH software version."); 672ca3176e7SBrian Feldman } else if (remote_minor == 3 || remote_minor == 4) { 673e8aafc91SKris Kennaway /* We speak 1.3, too. */ 674e8aafc91SKris Kennaway enable_compat13(); 675ca3176e7SBrian Feldman minor1 = 3; 676e8aafc91SKris Kennaway if (options.forward_agent) { 677cf2b5f3bSDag-Erling Smørgrav logit("Agent forwarding disabled for protocol 1.3"); 678e8aafc91SKris Kennaway options.forward_agent = 0; 679e8aafc91SKris Kennaway } 680e8aafc91SKris Kennaway } 681e8aafc91SKris Kennaway break; 682e8aafc91SKris Kennaway case 2: 683e8aafc91SKris Kennaway if (options.protocol & SSH_PROTO_2) { 684e8aafc91SKris Kennaway enable_compat20(); 685e8aafc91SKris Kennaway break; 686e8aafc91SKris Kennaway } 687e8aafc91SKris Kennaway /* FALLTHROUGH */ 688511b41d2SMark Murray default: 689e8aafc91SKris Kennaway mismatch = 1; 690e8aafc91SKris Kennaway break; 691511b41d2SMark Murray } 692e8aafc91SKris Kennaway if (mismatch) 693e8aafc91SKris Kennaway fatal("Protocol major versions differ: %d vs. %d", 694e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 695e8aafc91SKris Kennaway remote_major); 696f7167e0eSDag-Erling Smørgrav if ((datafellows & SSH_BUG_DERIVEKEY) != 0) 697f7167e0eSDag-Erling Smørgrav fatal("Server version \"%.100s\" uses unsafe key agreement; " 698f7167e0eSDag-Erling Smørgrav "refusing connection", remote_version); 699f7167e0eSDag-Erling Smørgrav if ((datafellows & SSH_BUG_RSASIGMD5) != 0) 700f7167e0eSDag-Erling Smørgrav logit("Server version \"%.100s\" uses unsafe RSA signature " 701f7167e0eSDag-Erling Smørgrav "scheme; disabling use of RSA keys", remote_version); 7026888a9beSDag-Erling Smørgrav if (!client_banner_sent) 7036888a9beSDag-Erling Smørgrav send_client_banner(connection_out, minor1); 704e8aafc91SKris Kennaway chop(server_version_string); 705511b41d2SMark Murray } 706511b41d2SMark Murray 707ca3176e7SBrian Feldman /* defaults to 'no' */ 708af12a3e7SDag-Erling Smørgrav static int 709af12a3e7SDag-Erling Smørgrav confirm(const char *prompt) 710511b41d2SMark Murray { 711af12a3e7SDag-Erling Smørgrav const char *msg, *again = "Please type 'yes' or 'no': "; 712af12a3e7SDag-Erling Smørgrav char *p; 713af12a3e7SDag-Erling Smørgrav int ret = -1; 714511b41d2SMark Murray 715ca3176e7SBrian Feldman if (options.batch_mode) 716ca3176e7SBrian Feldman return 0; 717af12a3e7SDag-Erling Smørgrav for (msg = prompt;;msg = again) { 718af12a3e7SDag-Erling Smørgrav p = read_passphrase(msg, RP_ECHO); 719af12a3e7SDag-Erling Smørgrav if (p == NULL || 720af12a3e7SDag-Erling Smørgrav (p[0] == '\0') || (p[0] == '\n') || 721af12a3e7SDag-Erling Smørgrav strncasecmp(p, "no", 2) == 0) 722af12a3e7SDag-Erling Smørgrav ret = 0; 723f388f5efSDag-Erling Smørgrav if (p && strncasecmp(p, "yes", 3) == 0) 724af12a3e7SDag-Erling Smørgrav ret = 1; 725e4a9863fSDag-Erling Smørgrav free(p); 726af12a3e7SDag-Erling Smørgrav if (ret != -1) 727af12a3e7SDag-Erling Smørgrav return ret; 728511b41d2SMark Murray } 729511b41d2SMark Murray } 730511b41d2SMark Murray 731b15c8340SDag-Erling Smørgrav static int 732b15c8340SDag-Erling Smørgrav check_host_cert(const char *host, const Key *host_key) 733b15c8340SDag-Erling Smørgrav { 734b15c8340SDag-Erling Smørgrav const char *reason; 735b15c8340SDag-Erling Smørgrav 736b15c8340SDag-Erling Smørgrav if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { 737b15c8340SDag-Erling Smørgrav error("%s", reason); 738b15c8340SDag-Erling Smørgrav return 0; 739b15c8340SDag-Erling Smørgrav } 740e2f6069cSDag-Erling Smørgrav if (buffer_len(&host_key->cert->critical) != 0) { 741e2f6069cSDag-Erling Smørgrav error("Certificate for %s contains unsupported " 742e2f6069cSDag-Erling Smørgrav "critical options(s)", host); 743b15c8340SDag-Erling Smørgrav return 0; 744b15c8340SDag-Erling Smørgrav } 745b15c8340SDag-Erling Smørgrav return 1; 746b15c8340SDag-Erling Smørgrav } 747b15c8340SDag-Erling Smørgrav 7484a421b63SDag-Erling Smørgrav static int 7494a421b63SDag-Erling Smørgrav sockaddr_is_local(struct sockaddr *hostaddr) 7504a421b63SDag-Erling Smørgrav { 7514a421b63SDag-Erling Smørgrav switch (hostaddr->sa_family) { 7524a421b63SDag-Erling Smørgrav case AF_INET: 7534a421b63SDag-Erling Smørgrav return (ntohl(((struct sockaddr_in *)hostaddr)-> 7544a421b63SDag-Erling Smørgrav sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 7554a421b63SDag-Erling Smørgrav case AF_INET6: 7564a421b63SDag-Erling Smørgrav return IN6_IS_ADDR_LOOPBACK( 7574a421b63SDag-Erling Smørgrav &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 7584a421b63SDag-Erling Smørgrav default: 7594a421b63SDag-Erling Smørgrav return 0; 7604a421b63SDag-Erling Smørgrav } 7614a421b63SDag-Erling Smørgrav } 7624a421b63SDag-Erling Smørgrav 7634a421b63SDag-Erling Smørgrav /* 7644a421b63SDag-Erling Smørgrav * Prepare the hostname and ip address strings that are used to lookup 7654a421b63SDag-Erling Smørgrav * host keys in known_hosts files. These may have a port number appended. 7664a421b63SDag-Erling Smørgrav */ 7674a421b63SDag-Erling Smørgrav void 7684a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, 7694a421b63SDag-Erling Smørgrav u_short port, char **hostfile_hostname, char **hostfile_ipaddr) 7704a421b63SDag-Erling Smørgrav { 7714a421b63SDag-Erling Smørgrav char ntop[NI_MAXHOST]; 7724a421b63SDag-Erling Smørgrav socklen_t addrlen; 7734a421b63SDag-Erling Smørgrav 7744a421b63SDag-Erling Smørgrav switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { 7754a421b63SDag-Erling Smørgrav case -1: 7764a421b63SDag-Erling Smørgrav addrlen = 0; 7774a421b63SDag-Erling Smørgrav break; 7784a421b63SDag-Erling Smørgrav case AF_INET: 7794a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr_in); 7804a421b63SDag-Erling Smørgrav break; 7814a421b63SDag-Erling Smørgrav case AF_INET6: 7824a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr_in6); 7834a421b63SDag-Erling Smørgrav break; 7844a421b63SDag-Erling Smørgrav default: 7854a421b63SDag-Erling Smørgrav addrlen = sizeof(struct sockaddr); 7864a421b63SDag-Erling Smørgrav break; 7874a421b63SDag-Erling Smørgrav } 7884a421b63SDag-Erling Smørgrav 7894a421b63SDag-Erling Smørgrav /* 7904a421b63SDag-Erling Smørgrav * We don't have the remote ip-address for connections 7914a421b63SDag-Erling Smørgrav * using a proxy command 7924a421b63SDag-Erling Smørgrav */ 7934a421b63SDag-Erling Smørgrav if (hostfile_ipaddr != NULL) { 7944a421b63SDag-Erling Smørgrav if (options.proxy_command == NULL) { 7954a421b63SDag-Erling Smørgrav if (getnameinfo(hostaddr, addrlen, 7964a421b63SDag-Erling Smørgrav ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) 7974a421b63SDag-Erling Smørgrav fatal("check_host_key: getnameinfo failed"); 7984a421b63SDag-Erling Smørgrav *hostfile_ipaddr = put_host_port(ntop, port); 7994a421b63SDag-Erling Smørgrav } else { 8004a421b63SDag-Erling Smørgrav *hostfile_ipaddr = xstrdup("<no hostip for proxy " 8014a421b63SDag-Erling Smørgrav "command>"); 8024a421b63SDag-Erling Smørgrav } 8034a421b63SDag-Erling Smørgrav } 8044a421b63SDag-Erling Smørgrav 8054a421b63SDag-Erling Smørgrav /* 8064a421b63SDag-Erling Smørgrav * Allow the user to record the key under a different name or 8074a421b63SDag-Erling Smørgrav * differentiate a non-standard port. This is useful for ssh 8084a421b63SDag-Erling Smørgrav * tunneling over forwarded connections or if you run multiple 8094a421b63SDag-Erling Smørgrav * sshd's on different ports on the same machine. 8104a421b63SDag-Erling Smørgrav */ 8114a421b63SDag-Erling Smørgrav if (hostfile_hostname != NULL) { 8124a421b63SDag-Erling Smørgrav if (options.host_key_alias != NULL) { 8134a421b63SDag-Erling Smørgrav *hostfile_hostname = xstrdup(options.host_key_alias); 8144a421b63SDag-Erling Smørgrav debug("using hostkeyalias: %s", *hostfile_hostname); 8154a421b63SDag-Erling Smørgrav } else { 8164a421b63SDag-Erling Smørgrav *hostfile_hostname = put_host_port(hostname, port); 8174a421b63SDag-Erling Smørgrav } 8184a421b63SDag-Erling Smørgrav } 8194a421b63SDag-Erling Smørgrav } 8204a421b63SDag-Erling Smørgrav 821e8aafc91SKris Kennaway /* 822af12a3e7SDag-Erling Smørgrav * check whether the supplied host key is valid, return -1 if the key 823e146993eSDag-Erling Smørgrav * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. 824e8aafc91SKris Kennaway */ 825333ee039SDag-Erling Smørgrav #define RDRW 0 826333ee039SDag-Erling Smørgrav #define RDONLY 1 827333ee039SDag-Erling Smørgrav #define ROQUIET 2 828af12a3e7SDag-Erling Smørgrav static int 829333ee039SDag-Erling Smørgrav check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 830e146993eSDag-Erling Smørgrav Key *host_key, int readonly, 831e146993eSDag-Erling Smørgrav char **user_hostfiles, u_int num_user_hostfiles, 832e146993eSDag-Erling Smørgrav char **system_hostfiles, u_int num_system_hostfiles) 833511b41d2SMark Murray { 834e8aafc91SKris Kennaway HostStatus host_status; 835e8aafc91SKris Kennaway HostStatus ip_status; 836e146993eSDag-Erling Smørgrav Key *raw_key = NULL; 837e146993eSDag-Erling Smørgrav char *ip = NULL, *host = NULL; 838e146993eSDag-Erling Smørgrav char hostline[1000], *hostp, *fp, *ra; 839af12a3e7SDag-Erling Smørgrav char msg[1024]; 840e146993eSDag-Erling Smørgrav const char *type; 8414a421b63SDag-Erling Smørgrav const struct hostkey_entry *host_found, *ip_found; 842e146993eSDag-Erling Smørgrav int len, cancelled_forwarding = 0; 843e146993eSDag-Erling Smørgrav int local = sockaddr_is_local(hostaddr); 844e146993eSDag-Erling Smørgrav int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; 845e146993eSDag-Erling Smørgrav struct hostkeys *host_hostkeys, *ip_hostkeys; 846e146993eSDag-Erling Smørgrav u_int i; 847511b41d2SMark Murray 848e8aafc91SKris Kennaway /* 849e8aafc91SKris Kennaway * Force accepting of the host key for loopback/localhost. The 850e8aafc91SKris Kennaway * problem is that if the home directory is NFS-mounted to multiple 851e8aafc91SKris Kennaway * machines, localhost will refer to a different machine in each of 852e8aafc91SKris Kennaway * them, and the user will get bogus HOST_CHANGED warnings. This 853e8aafc91SKris Kennaway * essentially disables host authentication for localhost; however, 854e8aafc91SKris Kennaway * this is probably not a real problem. 855e8aafc91SKris Kennaway */ 856af12a3e7SDag-Erling Smørgrav if (options.no_host_authentication_for_localhost == 1 && local && 857af12a3e7SDag-Erling Smørgrav options.host_key_alias == NULL) { 858ca3176e7SBrian Feldman debug("Forcing accepting of host key for " 859ca3176e7SBrian Feldman "loopback/localhost."); 860af12a3e7SDag-Erling Smørgrav return 0; 861511b41d2SMark Murray } 862511b41d2SMark Murray 863e8aafc91SKris Kennaway /* 8644a421b63SDag-Erling Smørgrav * Prepare the hostname and address strings used for hostkey lookup. 8654a421b63SDag-Erling Smørgrav * In some cases, these will have a port number appended. 866e8aafc91SKris Kennaway */ 8674a421b63SDag-Erling Smørgrav get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); 868d4af9e69SDag-Erling Smørgrav 869ca3176e7SBrian Feldman /* 870ca3176e7SBrian Feldman * Turn off check_host_ip if the connection is to localhost, via proxy 871ca3176e7SBrian Feldman * command or if we don't have a hostname to compare with 872ca3176e7SBrian Feldman */ 873333ee039SDag-Erling Smørgrav if (options.check_host_ip && (local || 874333ee039SDag-Erling Smørgrav strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 875ca3176e7SBrian Feldman options.check_host_ip = 0; 876ca3176e7SBrian Feldman 8774a421b63SDag-Erling Smørgrav host_hostkeys = init_hostkeys(); 878e146993eSDag-Erling Smørgrav for (i = 0; i < num_user_hostfiles; i++) 879e146993eSDag-Erling Smørgrav load_hostkeys(host_hostkeys, host, user_hostfiles[i]); 880e146993eSDag-Erling Smørgrav for (i = 0; i < num_system_hostfiles; i++) 881e146993eSDag-Erling Smørgrav load_hostkeys(host_hostkeys, host, system_hostfiles[i]); 8824a421b63SDag-Erling Smørgrav 8834a421b63SDag-Erling Smørgrav ip_hostkeys = NULL; 8844a421b63SDag-Erling Smørgrav if (!want_cert && options.check_host_ip) { 8854a421b63SDag-Erling Smørgrav ip_hostkeys = init_hostkeys(); 886e146993eSDag-Erling Smørgrav for (i = 0; i < num_user_hostfiles; i++) 887e146993eSDag-Erling Smørgrav load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]); 888e146993eSDag-Erling Smørgrav for (i = 0; i < num_system_hostfiles; i++) 889e146993eSDag-Erling Smørgrav load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]); 890e8aafc91SKris Kennaway } 891e8aafc91SKris Kennaway 892b15c8340SDag-Erling Smørgrav retry: 8934a421b63SDag-Erling Smørgrav /* Reload these as they may have changed on cert->key downgrade */ 894b15c8340SDag-Erling Smørgrav want_cert = key_is_cert(host_key); 895b15c8340SDag-Erling Smørgrav type = key_type(host_key); 896b15c8340SDag-Erling Smørgrav 897e8aafc91SKris Kennaway /* 898b74df5b2SDag-Erling Smørgrav * Check if the host key is present in the user's list of known 899e8aafc91SKris Kennaway * hosts or in the systemwide list. 900e8aafc91SKris Kennaway */ 9014a421b63SDag-Erling Smørgrav host_status = check_key_in_hostkeys(host_hostkeys, host_key, 9024a421b63SDag-Erling Smørgrav &host_found); 9034a421b63SDag-Erling Smørgrav 904e8aafc91SKris Kennaway /* 905e8aafc91SKris Kennaway * Also perform check for the ip address, skip the check if we are 906b15c8340SDag-Erling Smørgrav * localhost, looking for a certificate, or the hostname was an ip 907b15c8340SDag-Erling Smørgrav * address to begin with. 908e8aafc91SKris Kennaway */ 9094a421b63SDag-Erling Smørgrav if (!want_cert && ip_hostkeys != NULL) { 9104a421b63SDag-Erling Smørgrav ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, 9114a421b63SDag-Erling Smørgrav &ip_found); 912e8aafc91SKris Kennaway if (host_status == HOST_CHANGED && 9134a421b63SDag-Erling Smørgrav (ip_status != HOST_CHANGED || 9144a421b63SDag-Erling Smørgrav (ip_found != NULL && 9154a421b63SDag-Erling Smørgrav !key_equal(ip_found->key, host_found->key)))) 916e8aafc91SKris Kennaway host_ip_differ = 1; 917e8aafc91SKris Kennaway } else 918e8aafc91SKris Kennaway ip_status = host_status; 919e8aafc91SKris Kennaway 920e8aafc91SKris Kennaway switch (host_status) { 921e8aafc91SKris Kennaway case HOST_OK: 922e8aafc91SKris Kennaway /* The host is known and the key matches. */ 923b15c8340SDag-Erling Smørgrav debug("Host '%.200s' is known and matches the %s host %s.", 924b15c8340SDag-Erling Smørgrav host, type, want_cert ? "certificate" : "key"); 9254a421b63SDag-Erling Smørgrav debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", 9264a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 927b15c8340SDag-Erling Smørgrav if (want_cert && !check_host_cert(hostname, host_key)) 928b15c8340SDag-Erling Smørgrav goto fail; 929ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 930b15c8340SDag-Erling Smørgrav if (readonly || want_cert) 931cf2b5f3bSDag-Erling Smørgrav logit("%s host key for IP address " 932af12a3e7SDag-Erling Smørgrav "'%.128s' not in list of known hosts.", 933e8aafc91SKris Kennaway type, ip); 934e146993eSDag-Erling Smørgrav else if (!add_host_to_hostfile(user_hostfiles[0], ip, 935aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts)) 936cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the %s host key for IP " 937af12a3e7SDag-Erling Smørgrav "address '%.128s' to the list of known " 938e146993eSDag-Erling Smørgrav "hosts (%.30s).", type, ip, 939e146993eSDag-Erling Smørgrav user_hostfiles[0]); 940af12a3e7SDag-Erling Smørgrav else 941cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added the %s host " 942af12a3e7SDag-Erling Smørgrav "key for IP address '%.128s' to the list " 943af12a3e7SDag-Erling Smørgrav "of known hosts.", type, ip); 944d4af9e69SDag-Erling Smørgrav } else if (options.visual_host_key) { 945d4af9e69SDag-Erling Smørgrav fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 946d4af9e69SDag-Erling Smørgrav ra = key_fingerprint(host_key, SSH_FP_MD5, 947d4af9e69SDag-Erling Smørgrav SSH_FP_RANDOMART); 948d4af9e69SDag-Erling Smørgrav logit("Host key fingerprint is %s\n%s\n", fp, ra); 949e4a9863fSDag-Erling Smørgrav free(ra); 950e4a9863fSDag-Erling Smørgrav free(fp); 951e8aafc91SKris Kennaway } 952e8aafc91SKris Kennaway break; 953e8aafc91SKris Kennaway case HOST_NEW: 954333ee039SDag-Erling Smørgrav if (options.host_key_alias == NULL && port != 0 && 955333ee039SDag-Erling Smørgrav port != SSH_DEFAULT_PORT) { 956333ee039SDag-Erling Smørgrav debug("checking without port identifier"); 957cce7d346SDag-Erling Smørgrav if (check_host_key(hostname, hostaddr, 0, host_key, 958e146993eSDag-Erling Smørgrav ROQUIET, user_hostfiles, num_user_hostfiles, 959e146993eSDag-Erling Smørgrav system_hostfiles, num_system_hostfiles) == 0) { 960333ee039SDag-Erling Smørgrav debug("found matching key w/out port"); 961333ee039SDag-Erling Smørgrav break; 962333ee039SDag-Erling Smørgrav } 963333ee039SDag-Erling Smørgrav } 964b15c8340SDag-Erling Smørgrav if (readonly || want_cert) 965af12a3e7SDag-Erling Smørgrav goto fail; 966e8aafc91SKris Kennaway /* The host is new. */ 967e8aafc91SKris Kennaway if (options.strict_host_key_checking == 1) { 968af12a3e7SDag-Erling Smørgrav /* 969af12a3e7SDag-Erling Smørgrav * User has requested strict host key checking. We 970af12a3e7SDag-Erling Smørgrav * will not add the host key automatically. The only 971af12a3e7SDag-Erling Smørgrav * alternative left is to abort. 972af12a3e7SDag-Erling Smørgrav */ 973af12a3e7SDag-Erling Smørgrav error("No %s host key is known for %.200s and you " 974af12a3e7SDag-Erling Smørgrav "have requested strict checking.", type, host); 975af12a3e7SDag-Erling Smørgrav goto fail; 976e8aafc91SKris Kennaway } else if (options.strict_host_key_checking == 2) { 977cf2b5f3bSDag-Erling Smørgrav char msg1[1024], msg2[1024]; 978cf2b5f3bSDag-Erling Smørgrav 9794a421b63SDag-Erling Smørgrav if (show_other_keys(host_hostkeys, host_key)) 980cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), 981cf2b5f3bSDag-Erling Smørgrav "\nbut keys of different type are already" 982cf2b5f3bSDag-Erling Smørgrav " known for this host."); 983cf2b5f3bSDag-Erling Smørgrav else 984cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), "."); 985e8aafc91SKris Kennaway /* The default */ 986ca3176e7SBrian Feldman fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 987d4af9e69SDag-Erling Smørgrav ra = key_fingerprint(host_key, SSH_FP_MD5, 988d4af9e69SDag-Erling Smørgrav SSH_FP_RANDOMART); 989cf2b5f3bSDag-Erling Smørgrav msg2[0] = '\0'; 990cf2b5f3bSDag-Erling Smørgrav if (options.verify_host_key_dns) { 9911ec0d754SDag-Erling Smørgrav if (matching_host_key_dns) 992cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 993cf2b5f3bSDag-Erling Smørgrav "Matching host key fingerprint" 994cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 995cf2b5f3bSDag-Erling Smørgrav else 996cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 997cf2b5f3bSDag-Erling Smørgrav "No matching host key fingerprint" 998cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 999cf2b5f3bSDag-Erling Smørgrav } 1000af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 1001af12a3e7SDag-Erling Smørgrav "The authenticity of host '%.200s (%s)' can't be " 1002f388f5efSDag-Erling Smørgrav "established%s\n" 1003d4af9e69SDag-Erling Smørgrav "%s key fingerprint is %s.%s%s\n%s" 1004af12a3e7SDag-Erling Smørgrav "Are you sure you want to continue connecting " 1005f388f5efSDag-Erling Smørgrav "(yes/no)? ", 1006d4af9e69SDag-Erling Smørgrav host, ip, msg1, type, fp, 1007d4af9e69SDag-Erling Smørgrav options.visual_host_key ? "\n" : "", 1008d4af9e69SDag-Erling Smørgrav options.visual_host_key ? ra : "", 1009d4af9e69SDag-Erling Smørgrav msg2); 1010e4a9863fSDag-Erling Smørgrav free(ra); 1011e4a9863fSDag-Erling Smørgrav free(fp); 1012af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 1013af12a3e7SDag-Erling Smørgrav goto fail; 1014e8aafc91SKris Kennaway } 1015af12a3e7SDag-Erling Smørgrav /* 1016af12a3e7SDag-Erling Smørgrav * If not in strict mode, add the key automatically to the 1017af12a3e7SDag-Erling Smørgrav * local known_hosts file. 1018af12a3e7SDag-Erling Smørgrav */ 1019aa49c926SDag-Erling Smørgrav if (options.check_host_ip && ip_status == HOST_NEW) { 10204a421b63SDag-Erling Smørgrav snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 1021aa49c926SDag-Erling Smørgrav hostp = hostline; 1022aa49c926SDag-Erling Smørgrav if (options.hash_known_hosts) { 1023aa49c926SDag-Erling Smørgrav /* Add hash of host and IP separately */ 1024e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], 1025e146993eSDag-Erling Smørgrav host, host_key, options.hash_known_hosts) && 1026e146993eSDag-Erling Smørgrav add_host_to_hostfile(user_hostfiles[0], ip, 1027aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts); 1028aa49c926SDag-Erling Smørgrav } else { 1029aa49c926SDag-Erling Smørgrav /* Add unhashed "host,ip" */ 1030e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], 1031aa49c926SDag-Erling Smørgrav hostline, host_key, 1032aa49c926SDag-Erling Smørgrav options.hash_known_hosts); 1033aa49c926SDag-Erling Smørgrav } 1034aa49c926SDag-Erling Smørgrav } else { 1035e146993eSDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfiles[0], host, 1036e146993eSDag-Erling Smørgrav host_key, options.hash_known_hosts); 1037aa49c926SDag-Erling Smørgrav hostp = host; 1038aa49c926SDag-Erling Smørgrav } 1039aa49c926SDag-Erling Smørgrav 1040aa49c926SDag-Erling Smørgrav if (!r) 1041cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the host to the list of known " 1042e146993eSDag-Erling Smørgrav "hosts (%.500s).", user_hostfiles[0]); 1043e8aafc91SKris Kennaway else 1044cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added '%.200s' (%s) to the " 1045af12a3e7SDag-Erling Smørgrav "list of known hosts.", hostp, type); 1046e8aafc91SKris Kennaway break; 1047b15c8340SDag-Erling Smørgrav case HOST_REVOKED: 1048b15c8340SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1049b15c8340SDag-Erling Smørgrav error("@ WARNING: REVOKED HOST KEY DETECTED! @"); 1050b15c8340SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1051b15c8340SDag-Erling Smørgrav error("The %s host key for %s is marked as revoked.", type, host); 1052b15c8340SDag-Erling Smørgrav error("This could mean that a stolen key is being used to"); 1053b15c8340SDag-Erling Smørgrav error("impersonate this host."); 1054b15c8340SDag-Erling Smørgrav 1055b15c8340SDag-Erling Smørgrav /* 1056b15c8340SDag-Erling Smørgrav * If strict host key checking is in use, the user will have 1057b15c8340SDag-Erling Smørgrav * to edit the key manually and we can only abort. 1058b15c8340SDag-Erling Smørgrav */ 1059b15c8340SDag-Erling Smørgrav if (options.strict_host_key_checking) { 1060b15c8340SDag-Erling Smørgrav error("%s host key for %.200s was revoked and you have " 1061b15c8340SDag-Erling Smørgrav "requested strict checking.", type, host); 1062b15c8340SDag-Erling Smørgrav goto fail; 1063b15c8340SDag-Erling Smørgrav } 1064b15c8340SDag-Erling Smørgrav goto continue_unsafe; 1065b15c8340SDag-Erling Smørgrav 1066e8aafc91SKris Kennaway case HOST_CHANGED: 1067b15c8340SDag-Erling Smørgrav if (want_cert) { 1068b15c8340SDag-Erling Smørgrav /* 1069b15c8340SDag-Erling Smørgrav * This is only a debug() since it is valid to have 1070b15c8340SDag-Erling Smørgrav * CAs with wildcard DNS matches that don't match 1071b15c8340SDag-Erling Smørgrav * all hosts that one might visit. 1072b15c8340SDag-Erling Smørgrav */ 1073b15c8340SDag-Erling Smørgrav debug("Host certificate authority does not " 10744a421b63SDag-Erling Smørgrav "match %s in %s:%lu", CA_MARKER, 10754a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1076b15c8340SDag-Erling Smørgrav goto fail; 1077b15c8340SDag-Erling Smørgrav } 1078333ee039SDag-Erling Smørgrav if (readonly == ROQUIET) 1079333ee039SDag-Erling Smørgrav goto fail; 1080e8aafc91SKris Kennaway if (options.check_host_ip && host_ip_differ) { 108121e764dfSDag-Erling Smørgrav char *key_msg; 1082e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 108321e764dfSDag-Erling Smørgrav key_msg = "is unknown"; 1084e8aafc91SKris Kennaway else if (ip_status == HOST_OK) 108521e764dfSDag-Erling Smørgrav key_msg = "is unchanged"; 1086e8aafc91SKris Kennaway else 108721e764dfSDag-Erling Smørgrav key_msg = "has a different value"; 1088e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1089e8aafc91SKris Kennaway error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 1090e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 1091e8aafc91SKris Kennaway error("The %s host key for %s has changed,", type, host); 1092d4af9e69SDag-Erling Smørgrav error("and the key for the corresponding IP address %s", ip); 109321e764dfSDag-Erling Smørgrav error("%s. This could either mean that", key_msg); 1094e8aafc91SKris Kennaway error("DNS SPOOFING is happening or the IP address for the host"); 1095ca3176e7SBrian Feldman error("and its host key have changed at the same time."); 1096ca3176e7SBrian Feldman if (ip_status != HOST_NEW) 10974a421b63SDag-Erling Smørgrav error("Offending key for IP in %s:%lu", 10984a421b63SDag-Erling Smørgrav ip_found->file, ip_found->line); 1099e8aafc91SKris Kennaway } 1100e8aafc91SKris Kennaway /* The host key has changed. */ 11011ec0d754SDag-Erling Smørgrav warn_changed_key(host_key); 1102e8aafc91SKris Kennaway error("Add correct host key in %.100s to get rid of this message.", 1103e146993eSDag-Erling Smørgrav user_hostfiles[0]); 11044a421b63SDag-Erling Smørgrav error("Offending %s key in %s:%lu", key_type(host_found->key), 11054a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1106e8aafc91SKris Kennaway 1107e8aafc91SKris Kennaway /* 1108e8aafc91SKris Kennaway * If strict host key checking is in use, the user will have 1109e8aafc91SKris Kennaway * to edit the key manually and we can only abort. 1110e8aafc91SKris Kennaway */ 1111af12a3e7SDag-Erling Smørgrav if (options.strict_host_key_checking) { 1112af12a3e7SDag-Erling Smørgrav error("%s host key for %.200s has changed and you have " 1113af12a3e7SDag-Erling Smørgrav "requested strict checking.", type, host); 1114af12a3e7SDag-Erling Smørgrav goto fail; 1115af12a3e7SDag-Erling Smørgrav } 1116e8aafc91SKris Kennaway 1117b15c8340SDag-Erling Smørgrav continue_unsafe: 1118e8aafc91SKris Kennaway /* 1119e8aafc91SKris Kennaway * If strict host key checking has not been requested, allow 1120cf2b5f3bSDag-Erling Smørgrav * the connection but without MITM-able authentication or 1121333ee039SDag-Erling Smørgrav * forwarding. 1122e8aafc91SKris Kennaway */ 1123e8aafc91SKris Kennaway if (options.password_authentication) { 1124af12a3e7SDag-Erling Smørgrav error("Password authentication is disabled to avoid " 1125af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1126e8aafc91SKris Kennaway options.password_authentication = 0; 1127d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1128e8aafc91SKris Kennaway } 1129cf2b5f3bSDag-Erling Smørgrav if (options.kbd_interactive_authentication) { 1130cf2b5f3bSDag-Erling Smørgrav error("Keyboard-interactive authentication is disabled" 1131cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 1132cf2b5f3bSDag-Erling Smørgrav options.kbd_interactive_authentication = 0; 1133cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 1134d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1135cf2b5f3bSDag-Erling Smørgrav } 1136cf2b5f3bSDag-Erling Smørgrav if (options.challenge_response_authentication) { 1137cf2b5f3bSDag-Erling Smørgrav error("Challenge/response authentication is disabled" 1138cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 1139cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 1140d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1141cf2b5f3bSDag-Erling Smørgrav } 1142e8aafc91SKris Kennaway if (options.forward_agent) { 1143af12a3e7SDag-Erling Smørgrav error("Agent forwarding is disabled to avoid " 1144af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1145e8aafc91SKris Kennaway options.forward_agent = 0; 1146d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1147e8aafc91SKris Kennaway } 1148ca3176e7SBrian Feldman if (options.forward_x11) { 1149af12a3e7SDag-Erling Smørgrav error("X11 forwarding is disabled to avoid " 1150af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1151ca3176e7SBrian Feldman options.forward_x11 = 0; 1152d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1153ca3176e7SBrian Feldman } 1154af12a3e7SDag-Erling Smørgrav if (options.num_local_forwards > 0 || 1155af12a3e7SDag-Erling Smørgrav options.num_remote_forwards > 0) { 1156af12a3e7SDag-Erling Smørgrav error("Port forwarding is disabled to avoid " 1157af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 1158af12a3e7SDag-Erling Smørgrav options.num_local_forwards = 1159af12a3e7SDag-Erling Smørgrav options.num_remote_forwards = 0; 1160d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1161ca3176e7SBrian Feldman } 1162333ee039SDag-Erling Smørgrav if (options.tun_open != SSH_TUNMODE_NO) { 1163333ee039SDag-Erling Smørgrav error("Tunnel forwarding is disabled to avoid " 1164333ee039SDag-Erling Smørgrav "man-in-the-middle attacks."); 1165333ee039SDag-Erling Smørgrav options.tun_open = SSH_TUNMODE_NO; 1166d4af9e69SDag-Erling Smørgrav cancelled_forwarding = 1; 1167333ee039SDag-Erling Smørgrav } 1168d4af9e69SDag-Erling Smørgrav if (options.exit_on_forward_failure && cancelled_forwarding) 1169d4af9e69SDag-Erling Smørgrav fatal("Error: forwarding disabled due to host key " 1170d4af9e69SDag-Erling Smørgrav "check failure"); 1171d4af9e69SDag-Erling Smørgrav 1172e8aafc91SKris Kennaway /* 1173e8aafc91SKris Kennaway * XXX Should permit the user to change to use the new id. 1174e8aafc91SKris Kennaway * This could be done by converting the host key to an 1175e8aafc91SKris Kennaway * identifying sentence, tell that the host identifies itself 1176b15c8340SDag-Erling Smørgrav * by that sentence, and ask the user if he/she wishes to 1177e8aafc91SKris Kennaway * accept the authentication. 1178e8aafc91SKris Kennaway */ 1179e8aafc91SKris Kennaway break; 1180f388f5efSDag-Erling Smørgrav case HOST_FOUND: 1181f388f5efSDag-Erling Smørgrav fatal("internal error"); 1182f388f5efSDag-Erling Smørgrav break; 1183e8aafc91SKris Kennaway } 1184ca3176e7SBrian Feldman 1185ca3176e7SBrian Feldman if (options.check_host_ip && host_status != HOST_CHANGED && 1186ca3176e7SBrian Feldman ip_status == HOST_CHANGED) { 1187af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 1188af12a3e7SDag-Erling Smørgrav "Warning: the %s host key for '%.200s' " 1189af12a3e7SDag-Erling Smørgrav "differs from the key for the IP address '%.128s'" 11904a421b63SDag-Erling Smørgrav "\nOffending key for IP in %s:%lu", 11914a421b63SDag-Erling Smørgrav type, host, ip, ip_found->file, ip_found->line); 1192af12a3e7SDag-Erling Smørgrav if (host_status == HOST_OK) { 1193af12a3e7SDag-Erling Smørgrav len = strlen(msg); 1194af12a3e7SDag-Erling Smørgrav snprintf(msg + len, sizeof(msg) - len, 11954a421b63SDag-Erling Smørgrav "\nMatching host key in %s:%lu", 11964a421b63SDag-Erling Smørgrav host_found->file, host_found->line); 1197af12a3e7SDag-Erling Smørgrav } 1198ca3176e7SBrian Feldman if (options.strict_host_key_checking == 1) { 1199cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 1200af12a3e7SDag-Erling Smørgrav error("Exiting, you have requested strict checking."); 1201af12a3e7SDag-Erling Smørgrav goto fail; 1202ca3176e7SBrian Feldman } else if (options.strict_host_key_checking == 2) { 1203af12a3e7SDag-Erling Smørgrav strlcat(msg, "\nAre you sure you want " 1204af12a3e7SDag-Erling Smørgrav "to continue connecting (yes/no)? ", sizeof(msg)); 1205af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 1206af12a3e7SDag-Erling Smørgrav goto fail; 1207af12a3e7SDag-Erling Smørgrav } else { 1208cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 1209ca3176e7SBrian Feldman } 1210ca3176e7SBrian Feldman } 1211ca3176e7SBrian Feldman 1212e4a9863fSDag-Erling Smørgrav free(ip); 1213e4a9863fSDag-Erling Smørgrav free(host); 12144a421b63SDag-Erling Smørgrav if (host_hostkeys != NULL) 12154a421b63SDag-Erling Smørgrav free_hostkeys(host_hostkeys); 12164a421b63SDag-Erling Smørgrav if (ip_hostkeys != NULL) 12174a421b63SDag-Erling Smørgrav free_hostkeys(ip_hostkeys); 1218af12a3e7SDag-Erling Smørgrav return 0; 1219af12a3e7SDag-Erling Smørgrav 1220af12a3e7SDag-Erling Smørgrav fail: 1221b15c8340SDag-Erling Smørgrav if (want_cert && host_status != HOST_REVOKED) { 1222b15c8340SDag-Erling Smørgrav /* 1223b15c8340SDag-Erling Smørgrav * No matching certificate. Downgrade cert to raw key and 1224b15c8340SDag-Erling Smørgrav * search normally. 1225b15c8340SDag-Erling Smørgrav */ 1226b15c8340SDag-Erling Smørgrav debug("No matching CA found. Retry with plain key"); 1227b15c8340SDag-Erling Smørgrav raw_key = key_from_private(host_key); 1228b15c8340SDag-Erling Smørgrav if (key_drop_cert(raw_key) != 0) 1229b15c8340SDag-Erling Smørgrav fatal("Couldn't drop certificate"); 1230b15c8340SDag-Erling Smørgrav host_key = raw_key; 1231b15c8340SDag-Erling Smørgrav goto retry; 1232b15c8340SDag-Erling Smørgrav } 1233b15c8340SDag-Erling Smørgrav if (raw_key != NULL) 1234b15c8340SDag-Erling Smørgrav key_free(raw_key); 1235e4a9863fSDag-Erling Smørgrav free(ip); 1236e4a9863fSDag-Erling Smørgrav free(host); 12374a421b63SDag-Erling Smørgrav if (host_hostkeys != NULL) 12384a421b63SDag-Erling Smørgrav free_hostkeys(host_hostkeys); 12394a421b63SDag-Erling Smørgrav if (ip_hostkeys != NULL) 12404a421b63SDag-Erling Smørgrav free_hostkeys(ip_hostkeys); 1241af12a3e7SDag-Erling Smørgrav return -1; 1242e8aafc91SKris Kennaway } 1243511b41d2SMark Murray 1244cf2b5f3bSDag-Erling Smørgrav /* returns 0 if key verifies or -1 if key does NOT verify */ 1245fe5fd017SMark Murray int 1246af12a3e7SDag-Erling Smørgrav verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 1247fe5fd017SMark Murray { 12481ec0d754SDag-Erling Smørgrav int flags = 0; 12494a421b63SDag-Erling Smørgrav char *fp; 12504a421b63SDag-Erling Smørgrav 12514a421b63SDag-Erling Smørgrav fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 12524a421b63SDag-Erling Smørgrav debug("Server host key: %s %s", key_type(host_key), fp); 1253e4a9863fSDag-Erling Smørgrav free(fp); 1254fe5fd017SMark Murray 1255b15c8340SDag-Erling Smørgrav /* XXX certs are not yet supported for DNS */ 1256b15c8340SDag-Erling Smørgrav if (!key_is_cert(host_key) && options.verify_host_key_dns && 12571ec0d754SDag-Erling Smørgrav verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { 12581ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_FOUND) { 12591ec0d754SDag-Erling Smørgrav 12601ec0d754SDag-Erling Smørgrav if (options.verify_host_key_dns == 1 && 12611ec0d754SDag-Erling Smørgrav flags & DNS_VERIFY_MATCH && 12621ec0d754SDag-Erling Smørgrav flags & DNS_VERIFY_SECURE) 1263cf2b5f3bSDag-Erling Smørgrav return 0; 12641ec0d754SDag-Erling Smørgrav 12651ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_MATCH) { 12661ec0d754SDag-Erling Smørgrav matching_host_key_dns = 1; 12671ec0d754SDag-Erling Smørgrav } else { 12681ec0d754SDag-Erling Smørgrav warn_changed_key(host_key); 12691ec0d754SDag-Erling Smørgrav error("Update the SSHFP RR in DNS with the new " 12701ec0d754SDag-Erling Smørgrav "host key to get rid of this message."); 1271cf2b5f3bSDag-Erling Smørgrav } 1272cf2b5f3bSDag-Erling Smørgrav } 12731ec0d754SDag-Erling Smørgrav } 1274cf2b5f3bSDag-Erling Smørgrav 1275e146993eSDag-Erling Smørgrav return check_host_key(host, hostaddr, options.port, host_key, RDRW, 1276e146993eSDag-Erling Smørgrav options.user_hostfiles, options.num_user_hostfiles, 1277e146993eSDag-Erling Smørgrav options.system_hostfiles, options.num_system_hostfiles); 1278fe5fd017SMark Murray } 1279fe5fd017SMark Murray 1280511b41d2SMark Murray /* 1281511b41d2SMark Murray * Starts a dialog with the server, and authenticates the current user on the 1282511b41d2SMark Murray * server. This does not need any extra privileges. The basic connection 1283511b41d2SMark Murray * to the server must already have been established before this is called. 1284511b41d2SMark Murray * If login fails, this function prints an error and never returns. 1285511b41d2SMark Murray * This function does not require super-user privileges. 1286511b41d2SMark Murray */ 1287511b41d2SMark Murray void 128880628bacSDag-Erling Smørgrav ssh_login(Sensitive *sensitive, const char *orighost, 12894a421b63SDag-Erling Smørgrav struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) 1290511b41d2SMark Murray { 1291f7167e0eSDag-Erling Smørgrav char *host; 1292e8aafc91SKris Kennaway char *server_user, *local_user; 1293e8aafc91SKris Kennaway 1294e8aafc91SKris Kennaway local_user = xstrdup(pw->pw_name); 1295e8aafc91SKris Kennaway server_user = options.user ? options.user : local_user; 1296511b41d2SMark Murray 1297511b41d2SMark Murray /* Convert the user-supplied hostname into all lowercase. */ 1298511b41d2SMark Murray host = xstrdup(orighost); 1299f7167e0eSDag-Erling Smørgrav lowercase(host); 1300511b41d2SMark Murray 1301511b41d2SMark Murray /* Exchange protocol version identification strings with the server. */ 1302d4af9e69SDag-Erling Smørgrav ssh_exchange_identification(timeout_ms); 1303511b41d2SMark Murray 1304511b41d2SMark Murray /* Put the connection into non-blocking mode. */ 1305511b41d2SMark Murray packet_set_nonblocking(); 1306511b41d2SMark Murray 1307511b41d2SMark Murray /* key exchange */ 1308511b41d2SMark Murray /* authenticate user */ 1309e8aafc91SKris Kennaway if (compat20) { 13104a421b63SDag-Erling Smørgrav ssh_kex2(host, hostaddr, port); 131180628bacSDag-Erling Smørgrav ssh_userauth2(local_user, server_user, host, sensitive); 1312e8aafc91SKris Kennaway } else { 1313e8aafc91SKris Kennaway ssh_kex(host, hostaddr); 131480628bacSDag-Erling Smørgrav ssh_userauth1(local_user, server_user, host, sensitive); 1315e8aafc91SKris Kennaway } 1316e4a9863fSDag-Erling Smørgrav free(local_user); 1317511b41d2SMark Murray } 1318e0fbb1d2SBrian Feldman 1319e0fbb1d2SBrian Feldman void 1320e0fbb1d2SBrian Feldman ssh_put_password(char *password) 1321e0fbb1d2SBrian Feldman { 1322e0fbb1d2SBrian Feldman int size; 1323e0fbb1d2SBrian Feldman char *padded; 1324e0fbb1d2SBrian Feldman 1325ca3176e7SBrian Feldman if (datafellows & SSH_BUG_PASSWORDPAD) { 1326af12a3e7SDag-Erling Smørgrav packet_put_cstring(password); 1327ca3176e7SBrian Feldman return; 1328ca3176e7SBrian Feldman } 1329e0fbb1d2SBrian Feldman size = roundup(strlen(password) + 1, 32); 1330333ee039SDag-Erling Smørgrav padded = xcalloc(1, size); 1331e0fbb1d2SBrian Feldman strlcpy(padded, password, size); 1332e0fbb1d2SBrian Feldman packet_put_string(padded, size); 1333*b83788ffSDag-Erling Smørgrav explicit_bzero(padded, size); 1334e4a9863fSDag-Erling Smørgrav free(padded); 1335e0fbb1d2SBrian Feldman } 1336f388f5efSDag-Erling Smørgrav 1337f388f5efSDag-Erling Smørgrav /* print all known host keys for a given host, but skip keys of given type */ 1338f388f5efSDag-Erling Smørgrav static int 13394a421b63SDag-Erling Smørgrav show_other_keys(struct hostkeys *hostkeys, Key *key) 1340f388f5efSDag-Erling Smørgrav { 1341f7167e0eSDag-Erling Smørgrav int type[] = { 1342f7167e0eSDag-Erling Smørgrav KEY_RSA1, 1343f7167e0eSDag-Erling Smørgrav KEY_RSA, 1344f7167e0eSDag-Erling Smørgrav KEY_DSA, 1345f7167e0eSDag-Erling Smørgrav KEY_ECDSA, 1346f7167e0eSDag-Erling Smørgrav KEY_ED25519, 1347f7167e0eSDag-Erling Smørgrav -1 1348f7167e0eSDag-Erling Smørgrav }; 13494a421b63SDag-Erling Smørgrav int i, ret = 0; 13504a421b63SDag-Erling Smørgrav char *fp, *ra; 13514a421b63SDag-Erling Smørgrav const struct hostkey_entry *found; 1352f388f5efSDag-Erling Smørgrav 1353f388f5efSDag-Erling Smørgrav for (i = 0; type[i] != -1; i++) { 1354f388f5efSDag-Erling Smørgrav if (type[i] == key->type) 1355f388f5efSDag-Erling Smørgrav continue; 13564a421b63SDag-Erling Smørgrav if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) 1357f388f5efSDag-Erling Smørgrav continue; 13584a421b63SDag-Erling Smørgrav fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX); 13594a421b63SDag-Erling Smørgrav ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART); 13604a421b63SDag-Erling Smørgrav logit("WARNING: %s key found for host %s\n" 13614a421b63SDag-Erling Smørgrav "in %s:%lu\n" 13624a421b63SDag-Erling Smørgrav "%s key fingerprint %s.", 13634a421b63SDag-Erling Smørgrav key_type(found->key), 13644a421b63SDag-Erling Smørgrav found->host, found->file, found->line, 13654a421b63SDag-Erling Smørgrav key_type(found->key), fp); 13664a421b63SDag-Erling Smørgrav if (options.visual_host_key) 13674a421b63SDag-Erling Smørgrav logit("%s", ra); 1368e4a9863fSDag-Erling Smørgrav free(ra); 1369e4a9863fSDag-Erling Smørgrav free(fp); 13704a421b63SDag-Erling Smørgrav ret = 1; 1371f388f5efSDag-Erling Smørgrav } 13724a421b63SDag-Erling Smørgrav return ret; 1373f388f5efSDag-Erling Smørgrav } 13741ec0d754SDag-Erling Smørgrav 13751ec0d754SDag-Erling Smørgrav static void 13761ec0d754SDag-Erling Smørgrav warn_changed_key(Key *host_key) 13771ec0d754SDag-Erling Smørgrav { 13781ec0d754SDag-Erling Smørgrav char *fp; 13791ec0d754SDag-Erling Smørgrav 13801ec0d754SDag-Erling Smørgrav fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 13811ec0d754SDag-Erling Smørgrav 13821ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 13831ec0d754SDag-Erling Smørgrav error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 13841ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 13851ec0d754SDag-Erling Smørgrav error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 13861ec0d754SDag-Erling Smørgrav error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 13874a421b63SDag-Erling Smørgrav error("It is also possible that a host key has just been changed."); 13881ec0d754SDag-Erling Smørgrav error("The fingerprint for the %s key sent by the remote host is\n%s.", 13894a421b63SDag-Erling Smørgrav key_type(host_key), fp); 13901ec0d754SDag-Erling Smørgrav error("Please contact your system administrator."); 13911ec0d754SDag-Erling Smørgrav 1392e4a9863fSDag-Erling Smørgrav free(fp); 13931ec0d754SDag-Erling Smørgrav } 1394b74df5b2SDag-Erling Smørgrav 1395b74df5b2SDag-Erling Smørgrav /* 1396b74df5b2SDag-Erling Smørgrav * Execute a local command 1397b74df5b2SDag-Erling Smørgrav */ 1398b74df5b2SDag-Erling Smørgrav int 1399b74df5b2SDag-Erling Smørgrav ssh_local_cmd(const char *args) 1400b74df5b2SDag-Erling Smørgrav { 1401b74df5b2SDag-Erling Smørgrav char *shell; 1402b74df5b2SDag-Erling Smørgrav pid_t pid; 1403b74df5b2SDag-Erling Smørgrav int status; 14044a421b63SDag-Erling Smørgrav void (*osighand)(int); 1405b74df5b2SDag-Erling Smørgrav 1406b74df5b2SDag-Erling Smørgrav if (!options.permit_local_command || 1407b74df5b2SDag-Erling Smørgrav args == NULL || !*args) 1408b74df5b2SDag-Erling Smørgrav return (1); 1409b74df5b2SDag-Erling Smørgrav 14104a421b63SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL || *shell == '\0') 1411b74df5b2SDag-Erling Smørgrav shell = _PATH_BSHELL; 1412b74df5b2SDag-Erling Smørgrav 14134a421b63SDag-Erling Smørgrav osighand = signal(SIGCHLD, SIG_DFL); 1414b74df5b2SDag-Erling Smørgrav pid = fork(); 1415b74df5b2SDag-Erling Smørgrav if (pid == 0) { 14164a421b63SDag-Erling Smørgrav signal(SIGPIPE, SIG_DFL); 1417b74df5b2SDag-Erling Smørgrav debug3("Executing %s -c \"%s\"", shell, args); 1418b74df5b2SDag-Erling Smørgrav execl(shell, shell, "-c", args, (char *)NULL); 1419b74df5b2SDag-Erling Smørgrav error("Couldn't execute %s -c \"%s\": %s", 1420b74df5b2SDag-Erling Smørgrav shell, args, strerror(errno)); 1421b74df5b2SDag-Erling Smørgrav _exit(1); 1422b74df5b2SDag-Erling Smørgrav } else if (pid == -1) 1423b74df5b2SDag-Erling Smørgrav fatal("fork failed: %.100s", strerror(errno)); 1424b74df5b2SDag-Erling Smørgrav while (waitpid(pid, &status, 0) == -1) 1425b74df5b2SDag-Erling Smørgrav if (errno != EINTR) 1426b74df5b2SDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 14274a421b63SDag-Erling Smørgrav signal(SIGCHLD, osighand); 1428b74df5b2SDag-Erling Smørgrav 1429b74df5b2SDag-Erling Smørgrav if (!WIFEXITED(status)) 1430b74df5b2SDag-Erling Smørgrav return (1); 1431b74df5b2SDag-Erling Smørgrav 1432b74df5b2SDag-Erling Smørgrav return (WEXITSTATUS(status)); 1433b74df5b2SDag-Erling Smørgrav } 1434