162efe23aSDag-Erling Smørgrav /* $OpenBSD: sshconnect.c,v 1.200 2006/10/10 10:12:45 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" 17511b41d2SMark Murray 18333ee039SDag-Erling Smørgrav #include <sys/types.h> 19333ee039SDag-Erling Smørgrav #include <sys/wait.h> 20333ee039SDag-Erling Smørgrav #include <sys/stat.h> 21333ee039SDag-Erling Smørgrav #include <sys/socket.h> 22333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 23333ee039SDag-Erling Smørgrav # include <sys/time.h> 24333ee039SDag-Erling Smørgrav #endif 25e8aafc91SKris Kennaway 26333ee039SDag-Erling Smørgrav #include <netinet/in.h> 27333ee039SDag-Erling Smørgrav #include <arpa/inet.h> 28333ee039SDag-Erling Smørgrav 29333ee039SDag-Erling Smørgrav #include <ctype.h> 30333ee039SDag-Erling Smørgrav #include <errno.h> 31333ee039SDag-Erling Smørgrav #include <netdb.h> 32333ee039SDag-Erling Smørgrav #ifdef HAVE_PATHS_H 33333ee039SDag-Erling Smørgrav #include <paths.h> 34333ee039SDag-Erling Smørgrav #endif 35333ee039SDag-Erling Smørgrav #include <pwd.h> 36333ee039SDag-Erling Smørgrav #include <stdarg.h> 37333ee039SDag-Erling Smørgrav #include <stdio.h> 38333ee039SDag-Erling Smørgrav #include <stdlib.h> 39333ee039SDag-Erling Smørgrav #include <string.h> 40333ee039SDag-Erling Smørgrav #include <unistd.h> 41333ee039SDag-Erling Smørgrav 42511b41d2SMark Murray #include "xmalloc.h" 43333ee039SDag-Erling Smørgrav #include "key.h" 44333ee039SDag-Erling Smørgrav #include "hostfile.h" 45333ee039SDag-Erling Smørgrav #include "ssh.h" 46511b41d2SMark Murray #include "rsa.h" 47e8aafc91SKris Kennaway #include "buffer.h" 48511b41d2SMark Murray #include "packet.h" 49511b41d2SMark Murray #include "uidswap.h" 50511b41d2SMark Murray #include "compat.h" 513c6ae118SKris Kennaway #include "key.h" 52e8aafc91SKris Kennaway #include "sshconnect.h" 533c6ae118SKris Kennaway #include "hostfile.h" 54ca3176e7SBrian Feldman #include "log.h" 55ca3176e7SBrian Feldman #include "readconf.h" 56ca3176e7SBrian Feldman #include "atomicio.h" 57ca3176e7SBrian Feldman #include "misc.h" 58cf2b5f3bSDag-Erling Smørgrav #include "dns.h" 59333ee039SDag-Erling Smørgrav #include "version.h" 60cf2b5f3bSDag-Erling Smørgrav 61e8aafc91SKris Kennaway char *client_version_string = NULL; 62e8aafc91SKris Kennaway char *server_version_string = NULL; 63511b41d2SMark Murray 64b74df5b2SDag-Erling Smørgrav static int matching_host_key_dns = 0; 65cf2b5f3bSDag-Erling Smørgrav 6680628bacSDag-Erling Smørgrav /* import */ 67511b41d2SMark Murray extern Options options; 68511b41d2SMark Murray extern char *__progname; 6980628bacSDag-Erling Smørgrav extern uid_t original_real_uid; 7080628bacSDag-Erling Smørgrav extern uid_t original_effective_uid; 71f388f5efSDag-Erling Smørgrav extern pid_t proxy_command_pid; 72511b41d2SMark Murray 73989dd127SDag-Erling Smørgrav #ifndef INET6_ADDRSTRLEN /* for non IPv6 machines */ 74989dd127SDag-Erling Smørgrav #define INET6_ADDRSTRLEN 46 75989dd127SDag-Erling Smørgrav #endif 76989dd127SDag-Erling Smørgrav 77f388f5efSDag-Erling Smørgrav static int show_other_keys(const char *, Key *); 781ec0d754SDag-Erling Smørgrav static void warn_changed_key(Key *); 79ca3176e7SBrian Feldman 80511b41d2SMark Murray /* 81511b41d2SMark Murray * Connect to the given ssh server using a proxy command. 82511b41d2SMark Murray */ 83af12a3e7SDag-Erling Smørgrav static int 8480628bacSDag-Erling Smørgrav ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) 85511b41d2SMark Murray { 86d4ecd108SDag-Erling Smørgrav char *command_string, *tmp; 87511b41d2SMark Murray int pin[2], pout[2]; 88e8aafc91SKris Kennaway pid_t pid; 89511b41d2SMark Murray char strport[NI_MAXSERV]; 90511b41d2SMark Murray 91511b41d2SMark Murray /* Convert the port number into a string. */ 92511b41d2SMark Murray snprintf(strport, sizeof strport, "%hu", port); 93511b41d2SMark Murray 94f388f5efSDag-Erling Smørgrav /* 95f388f5efSDag-Erling Smørgrav * Build the final command string in the buffer by making the 96f388f5efSDag-Erling Smørgrav * appropriate substitutions to the given proxy command. 97f388f5efSDag-Erling Smørgrav * 98f388f5efSDag-Erling Smørgrav * Use "exec" to avoid "sh -c" processes on some platforms 99f388f5efSDag-Erling Smørgrav * (e.g. Solaris) 100f388f5efSDag-Erling Smørgrav */ 101333ee039SDag-Erling Smørgrav xasprintf(&tmp, "exec %s", proxy_command); 102d4ecd108SDag-Erling Smørgrav command_string = percent_expand(tmp, "h", host, 103d4ecd108SDag-Erling Smørgrav "p", strport, (char *)NULL); 104d4ecd108SDag-Erling Smørgrav xfree(tmp); 105511b41d2SMark Murray 106511b41d2SMark Murray /* Create pipes for communicating with the proxy. */ 107511b41d2SMark Murray if (pipe(pin) < 0 || pipe(pout) < 0) 108511b41d2SMark Murray fatal("Could not create pipes to communicate with the proxy: %.100s", 109511b41d2SMark Murray strerror(errno)); 110511b41d2SMark Murray 111511b41d2SMark Murray debug("Executing proxy command: %.500s", command_string); 112511b41d2SMark Murray 113511b41d2SMark Murray /* Fork and execute the proxy command. */ 114511b41d2SMark Murray if ((pid = fork()) == 0) { 115511b41d2SMark Murray char *argv[10]; 116511b41d2SMark Murray 117511b41d2SMark Murray /* Child. Permanently give up superuser privileges. */ 118333ee039SDag-Erling Smørgrav permanently_drop_suid(original_real_uid); 119511b41d2SMark Murray 120511b41d2SMark Murray /* Redirect stdin and stdout. */ 121511b41d2SMark Murray close(pin[1]); 122511b41d2SMark Murray if (pin[0] != 0) { 123511b41d2SMark Murray if (dup2(pin[0], 0) < 0) 124511b41d2SMark Murray perror("dup2 stdin"); 125511b41d2SMark Murray close(pin[0]); 126511b41d2SMark Murray } 127511b41d2SMark Murray close(pout[0]); 128511b41d2SMark Murray if (dup2(pout[1], 1) < 0) 129511b41d2SMark Murray perror("dup2 stdout"); 130511b41d2SMark Murray /* Cannot be 1 because pin allocated two descriptors. */ 131511b41d2SMark Murray close(pout[1]); 132511b41d2SMark Murray 133511b41d2SMark Murray /* Stderr is left as it is so that error messages get 134511b41d2SMark Murray printed on the user's terminal. */ 135ca3176e7SBrian Feldman argv[0] = _PATH_BSHELL; 136511b41d2SMark Murray argv[1] = "-c"; 137511b41d2SMark Murray argv[2] = command_string; 138511b41d2SMark Murray argv[3] = NULL; 139511b41d2SMark Murray 140511b41d2SMark Murray /* Execute the proxy command. Note that we gave up any 141511b41d2SMark Murray extra privileges above. */ 142ca3176e7SBrian Feldman execv(argv[0], argv); 143ca3176e7SBrian Feldman perror(argv[0]); 144511b41d2SMark Murray exit(1); 145511b41d2SMark Murray } 146511b41d2SMark Murray /* Parent. */ 147511b41d2SMark Murray if (pid < 0) 148511b41d2SMark Murray fatal("fork failed: %.100s", strerror(errno)); 149f388f5efSDag-Erling Smørgrav else 150f388f5efSDag-Erling Smørgrav proxy_command_pid = pid; /* save pid to clean up later */ 151511b41d2SMark Murray 152511b41d2SMark Murray /* Close child side of the descriptors. */ 153511b41d2SMark Murray close(pin[0]); 154511b41d2SMark Murray close(pout[1]); 155511b41d2SMark Murray 156511b41d2SMark Murray /* Free the command name. */ 157d4ecd108SDag-Erling Smørgrav xfree(command_string); 158511b41d2SMark Murray 159511b41d2SMark Murray /* Set the connection file descriptors. */ 160511b41d2SMark Murray packet_set_connection(pout[0], pin[1]); 161511b41d2SMark Murray 162af12a3e7SDag-Erling Smørgrav /* Indicate OK return */ 163af12a3e7SDag-Erling Smørgrav return 0; 164511b41d2SMark Murray } 165511b41d2SMark Murray 166511b41d2SMark Murray /* 167511b41d2SMark Murray * Creates a (possibly privileged) socket for use as the ssh connection. 168511b41d2SMark Murray */ 169af12a3e7SDag-Erling Smørgrav static int 170cf2b5f3bSDag-Erling Smørgrav ssh_create_socket(int privileged, struct addrinfo *ai) 171511b41d2SMark Murray { 172af12a3e7SDag-Erling Smørgrav int sock, gaierr; 173af12a3e7SDag-Erling Smørgrav struct addrinfo hints, *res; 174511b41d2SMark Murray 175511b41d2SMark Murray /* 176511b41d2SMark Murray * If we are running as root and want to connect to a privileged 177511b41d2SMark Murray * port, bind our own socket to a privileged port. 178511b41d2SMark Murray */ 179511b41d2SMark Murray if (privileged) { 180511b41d2SMark Murray int p = IPPORT_RESERVED - 1; 18180628bacSDag-Erling Smørgrav PRIV_START; 182cf2b5f3bSDag-Erling Smørgrav sock = rresvport_af(&p, ai->ai_family); 18380628bacSDag-Erling Smørgrav PRIV_END; 184511b41d2SMark Murray if (sock < 0) 185cf2b5f3bSDag-Erling Smørgrav error("rresvport: af=%d %.100s", ai->ai_family, 186cf2b5f3bSDag-Erling Smørgrav strerror(errno)); 187511b41d2SMark Murray else 188511b41d2SMark Murray debug("Allocated local port %d.", p); 189af12a3e7SDag-Erling Smørgrav return sock; 190af12a3e7SDag-Erling Smørgrav } 191cf2b5f3bSDag-Erling Smørgrav sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 192511b41d2SMark Murray if (sock < 0) 193511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 194af12a3e7SDag-Erling Smørgrav 195af12a3e7SDag-Erling Smørgrav /* Bind the socket to an alternative local IP address */ 196af12a3e7SDag-Erling Smørgrav if (options.bind_address == NULL) 197af12a3e7SDag-Erling Smørgrav return sock; 198af12a3e7SDag-Erling Smørgrav 199af12a3e7SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 200cf2b5f3bSDag-Erling Smørgrav hints.ai_family = ai->ai_family; 201cf2b5f3bSDag-Erling Smørgrav hints.ai_socktype = ai->ai_socktype; 202cf2b5f3bSDag-Erling Smørgrav hints.ai_protocol = ai->ai_protocol; 203af12a3e7SDag-Erling Smørgrav hints.ai_flags = AI_PASSIVE; 204af12a3e7SDag-Erling Smørgrav gaierr = getaddrinfo(options.bind_address, "0", &hints, &res); 205af12a3e7SDag-Erling Smørgrav if (gaierr) { 206af12a3e7SDag-Erling Smørgrav error("getaddrinfo: %s: %s", options.bind_address, 207af12a3e7SDag-Erling Smørgrav gai_strerror(gaierr)); 208af12a3e7SDag-Erling Smørgrav close(sock); 209af12a3e7SDag-Erling Smørgrav return -1; 210511b41d2SMark Murray } 211af12a3e7SDag-Erling Smørgrav if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 212af12a3e7SDag-Erling Smørgrav error("bind: %s: %s", options.bind_address, strerror(errno)); 213af12a3e7SDag-Erling Smørgrav close(sock); 214af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 215af12a3e7SDag-Erling Smørgrav return -1; 216af12a3e7SDag-Erling Smørgrav } 217af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 218511b41d2SMark Murray return sock; 219511b41d2SMark Murray } 220511b41d2SMark Murray 221cf2b5f3bSDag-Erling Smørgrav static int 222cf2b5f3bSDag-Erling Smørgrav timeout_connect(int sockfd, const struct sockaddr *serv_addr, 223cf2b5f3bSDag-Erling Smørgrav socklen_t addrlen, int timeout) 224cf2b5f3bSDag-Erling Smørgrav { 225cf2b5f3bSDag-Erling Smørgrav fd_set *fdset; 226cf2b5f3bSDag-Erling Smørgrav struct timeval tv; 227cf2b5f3bSDag-Erling Smørgrav socklen_t optlen; 228333ee039SDag-Erling Smørgrav int optval, rc, result = -1; 229cf2b5f3bSDag-Erling Smørgrav 230cf2b5f3bSDag-Erling Smørgrav if (timeout <= 0) 231cf2b5f3bSDag-Erling Smørgrav return (connect(sockfd, serv_addr, addrlen)); 232cf2b5f3bSDag-Erling Smørgrav 2331ec0d754SDag-Erling Smørgrav set_nonblock(sockfd); 234cf2b5f3bSDag-Erling Smørgrav rc = connect(sockfd, serv_addr, addrlen); 2351ec0d754SDag-Erling Smørgrav if (rc == 0) { 2361ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 237cf2b5f3bSDag-Erling Smørgrav return (0); 2381ec0d754SDag-Erling Smørgrav } 239cf2b5f3bSDag-Erling Smørgrav if (errno != EINPROGRESS) 240cf2b5f3bSDag-Erling Smørgrav return (-1); 241cf2b5f3bSDag-Erling Smørgrav 242333ee039SDag-Erling Smørgrav fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS), 243333ee039SDag-Erling Smørgrav sizeof(fd_mask)); 244cf2b5f3bSDag-Erling Smørgrav FD_SET(sockfd, fdset); 245cf2b5f3bSDag-Erling Smørgrav tv.tv_sec = timeout; 246cf2b5f3bSDag-Erling Smørgrav tv.tv_usec = 0; 247cf2b5f3bSDag-Erling Smørgrav 248cf2b5f3bSDag-Erling Smørgrav for (;;) { 249cf2b5f3bSDag-Erling Smørgrav rc = select(sockfd + 1, NULL, fdset, NULL, &tv); 250cf2b5f3bSDag-Erling Smørgrav if (rc != -1 || errno != EINTR) 251cf2b5f3bSDag-Erling Smørgrav break; 252cf2b5f3bSDag-Erling Smørgrav } 253cf2b5f3bSDag-Erling Smørgrav 254cf2b5f3bSDag-Erling Smørgrav switch (rc) { 255cf2b5f3bSDag-Erling Smørgrav case 0: 256cf2b5f3bSDag-Erling Smørgrav /* Timed out */ 257cf2b5f3bSDag-Erling Smørgrav errno = ETIMEDOUT; 258cf2b5f3bSDag-Erling Smørgrav break; 259cf2b5f3bSDag-Erling Smørgrav case -1: 260cf2b5f3bSDag-Erling Smørgrav /* Select error */ 261cf2b5f3bSDag-Erling Smørgrav debug("select: %s", strerror(errno)); 262cf2b5f3bSDag-Erling Smørgrav break; 263cf2b5f3bSDag-Erling Smørgrav case 1: 264cf2b5f3bSDag-Erling Smørgrav /* Completed or failed */ 265cf2b5f3bSDag-Erling Smørgrav optval = 0; 266cf2b5f3bSDag-Erling Smørgrav optlen = sizeof(optval); 267cf2b5f3bSDag-Erling Smørgrav if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, 268cf2b5f3bSDag-Erling Smørgrav &optlen) == -1) { 269cf2b5f3bSDag-Erling Smørgrav debug("getsockopt: %s", strerror(errno)); 270cf2b5f3bSDag-Erling Smørgrav break; 271cf2b5f3bSDag-Erling Smørgrav } 272cf2b5f3bSDag-Erling Smørgrav if (optval != 0) { 273cf2b5f3bSDag-Erling Smørgrav errno = optval; 274cf2b5f3bSDag-Erling Smørgrav break; 275cf2b5f3bSDag-Erling Smørgrav } 276cf2b5f3bSDag-Erling Smørgrav result = 0; 2771ec0d754SDag-Erling Smørgrav unset_nonblock(sockfd); 278cf2b5f3bSDag-Erling Smørgrav break; 279cf2b5f3bSDag-Erling Smørgrav default: 280cf2b5f3bSDag-Erling Smørgrav /* Should not occur */ 281cf2b5f3bSDag-Erling Smørgrav fatal("Bogus return (%d) from select()", rc); 282cf2b5f3bSDag-Erling Smørgrav } 283cf2b5f3bSDag-Erling Smørgrav 284cf2b5f3bSDag-Erling Smørgrav xfree(fdset); 285cf2b5f3bSDag-Erling Smørgrav return (result); 286cf2b5f3bSDag-Erling Smørgrav } 287cf2b5f3bSDag-Erling Smørgrav 288511b41d2SMark Murray /* 289511b41d2SMark Murray * Opens a TCP/IP connection to the remote server on the given host. 290511b41d2SMark Murray * The address of the remote host will be returned in hostaddr. 29180628bacSDag-Erling Smørgrav * If port is 0, the default port will be used. If needpriv is true, 292511b41d2SMark Murray * a privileged port will be allocated to make the connection. 29380628bacSDag-Erling Smørgrav * This requires super-user privileges if needpriv is true. 294511b41d2SMark Murray * Connection_attempts specifies the maximum number of tries (one per 295511b41d2SMark Murray * second). If proxy_command is non-NULL, it specifies the command (with %h 296511b41d2SMark Murray * and %p substituted for host and port, respectively) to use to contact 297511b41d2SMark Murray * the daemon. 298511b41d2SMark Murray */ 299511b41d2SMark Murray int 3001f5ce8f4SBrian Feldman ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 301af12a3e7SDag-Erling Smørgrav u_short port, int family, int connection_attempts, 30280628bacSDag-Erling Smørgrav int needpriv, const char *proxy_command) 303511b41d2SMark Murray { 304511b41d2SMark Murray int gaierr; 305ca3176e7SBrian Feldman int on = 1; 306ca3176e7SBrian Feldman int sock = -1, attempt; 307ca3176e7SBrian Feldman char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 308ca3176e7SBrian Feldman struct addrinfo hints, *ai, *aitop; 309511b41d2SMark Murray 310e73e9afaSDag-Erling Smørgrav debug2("ssh_connect: needpriv %d", needpriv); 311511b41d2SMark Murray 312511b41d2SMark Murray /* If a proxy command is given, connect using it. */ 313511b41d2SMark Murray if (proxy_command != NULL) 31480628bacSDag-Erling Smørgrav return ssh_proxy_connect(host, port, proxy_command); 315511b41d2SMark Murray 316511b41d2SMark Murray /* No proxy command. */ 317511b41d2SMark Murray 318511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 319af12a3e7SDag-Erling Smørgrav hints.ai_family = family; 320511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 321a82e551fSDag-Erling Smørgrav snprintf(strport, sizeof strport, "%u", port); 3221f5ce8f4SBrian Feldman if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 3231f5ce8f4SBrian Feldman fatal("%s: %.100s: %s", __progname, host, 324511b41d2SMark Murray gai_strerror(gaierr)); 325511b41d2SMark Murray 326333ee039SDag-Erling Smørgrav for (attempt = 0; attempt < connection_attempts; attempt++) { 32762efe23aSDag-Erling Smørgrav if (attempt > 0) { 32862efe23aSDag-Erling Smørgrav /* Sleep a moment before retrying. */ 32962efe23aSDag-Erling Smørgrav sleep(1); 330511b41d2SMark Murray debug("Trying again..."); 33162efe23aSDag-Erling Smørgrav } 332333ee039SDag-Erling Smørgrav /* 333333ee039SDag-Erling Smørgrav * Loop through addresses for this host, and try each one in 334333ee039SDag-Erling Smørgrav * sequence until the connection succeeds. 335333ee039SDag-Erling Smørgrav */ 336511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 337511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 338511b41d2SMark Murray continue; 339511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 340511b41d2SMark Murray ntop, sizeof(ntop), strport, sizeof(strport), 341511b41d2SMark Murray NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 342511b41d2SMark Murray error("ssh_connect: getnameinfo failed"); 343511b41d2SMark Murray continue; 344511b41d2SMark Murray } 345511b41d2SMark Murray debug("Connecting to %.200s [%.100s] port %s.", 3461f5ce8f4SBrian Feldman host, ntop, strport); 347511b41d2SMark Murray 348511b41d2SMark Murray /* Create a socket for connecting. */ 349cf2b5f3bSDag-Erling Smørgrav sock = ssh_create_socket(needpriv, ai); 350511b41d2SMark Murray if (sock < 0) 351af12a3e7SDag-Erling Smørgrav /* Any error is already output */ 352511b41d2SMark Murray continue; 353511b41d2SMark Murray 354cf2b5f3bSDag-Erling Smørgrav if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, 355cf2b5f3bSDag-Erling Smørgrav options.connection_timeout) >= 0) { 356511b41d2SMark Murray /* Successful connection. */ 357c322fe35SKris Kennaway memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 358511b41d2SMark Murray break; 359511b41d2SMark Murray } else { 360f388f5efSDag-Erling Smørgrav debug("connect to address %s port %s: %s", 361f388f5efSDag-Erling Smørgrav ntop, strport, strerror(errno)); 362511b41d2SMark Murray close(sock); 363333ee039SDag-Erling Smørgrav sock = -1; 364511b41d2SMark Murray } 365511b41d2SMark Murray } 366333ee039SDag-Erling Smørgrav if (sock != -1) 367511b41d2SMark Murray break; /* Successful connection. */ 368511b41d2SMark Murray } 369511b41d2SMark Murray 370511b41d2SMark Murray freeaddrinfo(aitop); 371511b41d2SMark Murray 372511b41d2SMark Murray /* Return failure if we didn't get a successful connection. */ 373333ee039SDag-Erling Smørgrav if (sock == -1) { 374aa49c926SDag-Erling Smørgrav error("ssh: connect to host %s port %s: %s", 375f388f5efSDag-Erling Smørgrav host, strport, strerror(errno)); 376aa49c926SDag-Erling Smørgrav return (-1); 377f388f5efSDag-Erling Smørgrav } 378511b41d2SMark Murray 379511b41d2SMark Murray debug("Connection established."); 380511b41d2SMark Murray 3811ec0d754SDag-Erling Smørgrav /* Set SO_KEEPALIVE if requested. */ 3821ec0d754SDag-Erling Smørgrav if (options.tcp_keep_alive && 383ca3176e7SBrian Feldman setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 384ca3176e7SBrian Feldman sizeof(on)) < 0) 385ca3176e7SBrian Feldman error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 386ca3176e7SBrian Feldman 387511b41d2SMark Murray /* Set the connection. */ 388511b41d2SMark Murray packet_set_connection(sock, sock); 389511b41d2SMark Murray 390af12a3e7SDag-Erling Smørgrav return 0; 391511b41d2SMark Murray } 392511b41d2SMark Murray 393511b41d2SMark Murray /* 394e8aafc91SKris Kennaway * Waits for the server identification string, and sends our own 395e8aafc91SKris Kennaway * identification string. 396511b41d2SMark Murray */ 397af12a3e7SDag-Erling Smørgrav static void 398ca3176e7SBrian Feldman ssh_exchange_identification(void) 399511b41d2SMark Murray { 400e8aafc91SKris Kennaway char buf[256], remote_version[256]; /* must be same size! */ 401d4ecd108SDag-Erling Smørgrav int remote_major, remote_minor, mismatch; 402e8aafc91SKris Kennaway int connection_in = packet_get_connection_in(); 403e8aafc91SKris Kennaway int connection_out = packet_get_connection_out(); 404ca3176e7SBrian Feldman int minor1 = PROTOCOL_MINOR_1; 405333ee039SDag-Erling Smørgrav u_int i, n; 406511b41d2SMark Murray 407d4ecd108SDag-Erling Smørgrav /* Read other side's version identification. */ 408333ee039SDag-Erling Smørgrav for (n = 0;;) { 409e8aafc91SKris Kennaway for (i = 0; i < sizeof(buf) - 1; i++) { 410d4ecd108SDag-Erling Smørgrav size_t len = atomicio(read, connection_in, &buf[i], 1); 411d4ecd108SDag-Erling Smørgrav 412d4ecd108SDag-Erling Smørgrav if (len != 1 && errno == EPIPE) 413e8aafc91SKris Kennaway fatal("ssh_exchange_identification: Connection closed by remote host"); 414d4ecd108SDag-Erling Smørgrav else if (len != 1) 415d4ecd108SDag-Erling Smørgrav fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); 416e8aafc91SKris Kennaway if (buf[i] == '\r') { 417e8aafc91SKris Kennaway buf[i] = '\n'; 418e8aafc91SKris Kennaway buf[i + 1] = 0; 419e8aafc91SKris Kennaway continue; /**XXX wait for \n */ 420511b41d2SMark Murray } 421e8aafc91SKris Kennaway if (buf[i] == '\n') { 422e8aafc91SKris Kennaway buf[i + 1] = 0; 423511b41d2SMark Murray break; 424e8aafc91SKris Kennaway } 425333ee039SDag-Erling Smørgrav if (++n > 65536) 426333ee039SDag-Erling Smørgrav fatal("ssh_exchange_identification: No banner received"); 427e8aafc91SKris Kennaway } 428e8aafc91SKris Kennaway buf[sizeof(buf) - 1] = 0; 429c2d3a559SKris Kennaway if (strncmp(buf, "SSH-", 4) == 0) 430c2d3a559SKris Kennaway break; 431c2d3a559SKris Kennaway debug("ssh_exchange_identification: %s", buf); 432c2d3a559SKris Kennaway } 433e8aafc91SKris Kennaway server_version_string = xstrdup(buf); 434511b41d2SMark Murray 435511b41d2SMark Murray /* 436e8aafc91SKris Kennaway * Check that the versions match. In future this might accept 437e8aafc91SKris Kennaway * several versions and set appropriate flags to handle them. 438511b41d2SMark Murray */ 439e8aafc91SKris Kennaway if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 440e8aafc91SKris Kennaway &remote_major, &remote_minor, remote_version) != 3) 441e8aafc91SKris Kennaway fatal("Bad remote protocol version identification: '%.100s'", buf); 442e8aafc91SKris Kennaway debug("Remote protocol version %d.%d, remote software version %.100s", 443e8aafc91SKris Kennaway remote_major, remote_minor, remote_version); 444511b41d2SMark Murray 445e8aafc91SKris Kennaway compat_datafellows(remote_version); 446e8aafc91SKris Kennaway mismatch = 0; 447e8aafc91SKris Kennaway 448e8aafc91SKris Kennaway switch (remote_major) { 449e8aafc91SKris Kennaway case 1: 450e8aafc91SKris Kennaway if (remote_minor == 99 && 451e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) && 452e8aafc91SKris Kennaway !(options.protocol & SSH_PROTO_1_PREFERRED)) { 453e8aafc91SKris Kennaway enable_compat20(); 454511b41d2SMark Murray break; 455e8aafc91SKris Kennaway } 456e8aafc91SKris Kennaway if (!(options.protocol & SSH_PROTO_1)) { 457e8aafc91SKris Kennaway mismatch = 1; 458e8aafc91SKris Kennaway break; 459e8aafc91SKris Kennaway } 460e8aafc91SKris Kennaway if (remote_minor < 3) { 461e8aafc91SKris Kennaway fatal("Remote machine has too old SSH software version."); 462ca3176e7SBrian Feldman } else if (remote_minor == 3 || remote_minor == 4) { 463e8aafc91SKris Kennaway /* We speak 1.3, too. */ 464e8aafc91SKris Kennaway enable_compat13(); 465ca3176e7SBrian Feldman minor1 = 3; 466e8aafc91SKris Kennaway if (options.forward_agent) { 467cf2b5f3bSDag-Erling Smørgrav logit("Agent forwarding disabled for protocol 1.3"); 468e8aafc91SKris Kennaway options.forward_agent = 0; 469e8aafc91SKris Kennaway } 470e8aafc91SKris Kennaway } 471e8aafc91SKris Kennaway break; 472e8aafc91SKris Kennaway case 2: 473e8aafc91SKris Kennaway if (options.protocol & SSH_PROTO_2) { 474e8aafc91SKris Kennaway enable_compat20(); 475e8aafc91SKris Kennaway break; 476e8aafc91SKris Kennaway } 477e8aafc91SKris Kennaway /* FALLTHROUGH */ 478511b41d2SMark Murray default: 479e8aafc91SKris Kennaway mismatch = 1; 480e8aafc91SKris Kennaway break; 481511b41d2SMark Murray } 482e8aafc91SKris Kennaway if (mismatch) 483e8aafc91SKris Kennaway fatal("Protocol major versions differ: %d vs. %d", 484e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 485e8aafc91SKris Kennaway remote_major); 486e8aafc91SKris Kennaway /* Send our own protocol version identification. */ 487e8aafc91SKris Kennaway snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 488e8aafc91SKris Kennaway compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 489ca3176e7SBrian Feldman compat20 ? PROTOCOL_MINOR_2 : minor1, 490e8aafc91SKris Kennaway SSH_VERSION); 491cf2b5f3bSDag-Erling Smørgrav if (atomicio(vwrite, connection_out, buf, strlen(buf)) != strlen(buf)) 492e8aafc91SKris Kennaway fatal("write: %.100s", strerror(errno)); 493e8aafc91SKris Kennaway client_version_string = xstrdup(buf); 494e8aafc91SKris Kennaway chop(client_version_string); 495e8aafc91SKris Kennaway chop(server_version_string); 496e8aafc91SKris Kennaway debug("Local version string %.100s", client_version_string); 497511b41d2SMark Murray } 498511b41d2SMark Murray 499ca3176e7SBrian Feldman /* defaults to 'no' */ 500af12a3e7SDag-Erling Smørgrav static int 501af12a3e7SDag-Erling Smørgrav confirm(const char *prompt) 502511b41d2SMark Murray { 503af12a3e7SDag-Erling Smørgrav const char *msg, *again = "Please type 'yes' or 'no': "; 504af12a3e7SDag-Erling Smørgrav char *p; 505af12a3e7SDag-Erling Smørgrav int ret = -1; 506511b41d2SMark Murray 507ca3176e7SBrian Feldman if (options.batch_mode) 508ca3176e7SBrian Feldman return 0; 509af12a3e7SDag-Erling Smørgrav for (msg = prompt;;msg = again) { 510af12a3e7SDag-Erling Smørgrav p = read_passphrase(msg, RP_ECHO); 511af12a3e7SDag-Erling Smørgrav if (p == NULL || 512af12a3e7SDag-Erling Smørgrav (p[0] == '\0') || (p[0] == '\n') || 513af12a3e7SDag-Erling Smørgrav strncasecmp(p, "no", 2) == 0) 514af12a3e7SDag-Erling Smørgrav ret = 0; 515f388f5efSDag-Erling Smørgrav if (p && strncasecmp(p, "yes", 3) == 0) 516af12a3e7SDag-Erling Smørgrav ret = 1; 517af12a3e7SDag-Erling Smørgrav if (p) 518af12a3e7SDag-Erling Smørgrav xfree(p); 519af12a3e7SDag-Erling Smørgrav if (ret != -1) 520af12a3e7SDag-Erling Smørgrav return ret; 521511b41d2SMark Murray } 522511b41d2SMark Murray } 523511b41d2SMark Murray 524e8aafc91SKris Kennaway /* 525af12a3e7SDag-Erling Smørgrav * check whether the supplied host key is valid, return -1 if the key 526af12a3e7SDag-Erling Smørgrav * is not valid. the user_hostfile will not be updated if 'readonly' is true. 527e8aafc91SKris Kennaway */ 528333ee039SDag-Erling Smørgrav #define RDRW 0 529333ee039SDag-Erling Smørgrav #define RDONLY 1 530333ee039SDag-Erling Smørgrav #define ROQUIET 2 531af12a3e7SDag-Erling Smørgrav static int 532333ee039SDag-Erling Smørgrav check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 533333ee039SDag-Erling Smørgrav Key *host_key, int readonly, const char *user_hostfile, 534333ee039SDag-Erling Smørgrav const char *system_hostfile) 535511b41d2SMark Murray { 536e8aafc91SKris Kennaway Key *file_key; 5371ec0d754SDag-Erling Smørgrav const char *type = key_type(host_key); 538333ee039SDag-Erling Smørgrav char *ip = NULL, *host = NULL; 539ca3176e7SBrian Feldman char hostline[1000], *hostp, *fp; 540e8aafc91SKris Kennaway HostStatus host_status; 541e8aafc91SKris Kennaway HostStatus ip_status; 542aa49c926SDag-Erling Smørgrav int r, local = 0, host_ip_differ = 0; 543989dd127SDag-Erling Smørgrav int salen; 544e8aafc91SKris Kennaway char ntop[NI_MAXHOST]; 545af12a3e7SDag-Erling Smørgrav char msg[1024]; 546cf2b5f3bSDag-Erling Smørgrav int len, host_line, ip_line; 547ca3176e7SBrian Feldman const char *host_file = NULL, *ip_file = NULL; 548511b41d2SMark Murray 549e8aafc91SKris Kennaway /* 550e8aafc91SKris Kennaway * Force accepting of the host key for loopback/localhost. The 551e8aafc91SKris Kennaway * problem is that if the home directory is NFS-mounted to multiple 552e8aafc91SKris Kennaway * machines, localhost will refer to a different machine in each of 553e8aafc91SKris Kennaway * them, and the user will get bogus HOST_CHANGED warnings. This 554e8aafc91SKris Kennaway * essentially disables host authentication for localhost; however, 555e8aafc91SKris Kennaway * this is probably not a real problem. 556e8aafc91SKris Kennaway */ 557e8aafc91SKris Kennaway /** hostaddr == 0! */ 558e8aafc91SKris Kennaway switch (hostaddr->sa_family) { 559e8aafc91SKris Kennaway case AF_INET: 560af12a3e7SDag-Erling Smørgrav local = (ntohl(((struct sockaddr_in *)hostaddr)-> 561af12a3e7SDag-Erling Smørgrav sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 562989dd127SDag-Erling Smørgrav salen = sizeof(struct sockaddr_in); 563511b41d2SMark Murray break; 564e8aafc91SKris Kennaway case AF_INET6: 565af12a3e7SDag-Erling Smørgrav local = IN6_IS_ADDR_LOOPBACK( 566af12a3e7SDag-Erling Smørgrav &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 567989dd127SDag-Erling Smørgrav salen = sizeof(struct sockaddr_in6); 568511b41d2SMark Murray break; 569e8aafc91SKris Kennaway default: 570e8aafc91SKris Kennaway local = 0; 571989dd127SDag-Erling Smørgrav salen = sizeof(struct sockaddr_storage); 572511b41d2SMark Murray break; 573511b41d2SMark Murray } 574af12a3e7SDag-Erling Smørgrav if (options.no_host_authentication_for_localhost == 1 && local && 575af12a3e7SDag-Erling Smørgrav options.host_key_alias == NULL) { 576ca3176e7SBrian Feldman debug("Forcing accepting of host key for " 577ca3176e7SBrian Feldman "loopback/localhost."); 578af12a3e7SDag-Erling Smørgrav return 0; 579511b41d2SMark Murray } 580511b41d2SMark Murray 581e8aafc91SKris Kennaway /* 582ca3176e7SBrian Feldman * We don't have the remote ip-address for connections 583ca3176e7SBrian Feldman * using a proxy command 584e8aafc91SKris Kennaway */ 585ca3176e7SBrian Feldman if (options.proxy_command == NULL) { 586989dd127SDag-Erling Smørgrav if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop), 587e8aafc91SKris Kennaway NULL, 0, NI_NUMERICHOST) != 0) 588e8aafc91SKris Kennaway fatal("check_host_key: getnameinfo failed"); 589333ee039SDag-Erling Smørgrav ip = put_host_port(ntop, port); 590ca3176e7SBrian Feldman } else { 591ca3176e7SBrian Feldman ip = xstrdup("<no hostip for proxy command>"); 592ca3176e7SBrian Feldman } 593ca3176e7SBrian Feldman /* 594ca3176e7SBrian Feldman * Turn off check_host_ip if the connection is to localhost, via proxy 595ca3176e7SBrian Feldman * command or if we don't have a hostname to compare with 596ca3176e7SBrian Feldman */ 597333ee039SDag-Erling Smørgrav if (options.check_host_ip && (local || 598333ee039SDag-Erling Smørgrav strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 599ca3176e7SBrian Feldman options.check_host_ip = 0; 600ca3176e7SBrian Feldman 601ca3176e7SBrian Feldman /* 602333ee039SDag-Erling Smørgrav * Allow the user to record the key under a different name or 603333ee039SDag-Erling Smørgrav * differentiate a non-standard port. This is useful for ssh 604333ee039SDag-Erling Smørgrav * tunneling over forwarded connections or if you run multiple 605333ee039SDag-Erling Smørgrav * sshd's on different ports on the same machine. 606ca3176e7SBrian Feldman */ 607ca3176e7SBrian Feldman if (options.host_key_alias != NULL) { 608333ee039SDag-Erling Smørgrav host = xstrdup(options.host_key_alias); 609ca3176e7SBrian Feldman debug("using hostkeyalias: %s", host); 610333ee039SDag-Erling Smørgrav } else { 611333ee039SDag-Erling Smørgrav host = put_host_port(hostname, port); 612e8aafc91SKris Kennaway } 613e8aafc91SKris Kennaway 614e8aafc91SKris Kennaway /* 615e8aafc91SKris Kennaway * Store the host key from the known host file in here so that we can 616e8aafc91SKris Kennaway * compare it with the key for the IP address. 617e8aafc91SKris Kennaway */ 618e8aafc91SKris Kennaway file_key = key_new(host_key->type); 619e8aafc91SKris Kennaway 620e8aafc91SKris Kennaway /* 621b74df5b2SDag-Erling Smørgrav * Check if the host key is present in the user's list of known 622e8aafc91SKris Kennaway * hosts or in the systemwide list. 623e8aafc91SKris Kennaway */ 624ca3176e7SBrian Feldman host_file = user_hostfile; 625af12a3e7SDag-Erling Smørgrav host_status = check_host_in_hostfile(host_file, host, host_key, 626af12a3e7SDag-Erling Smørgrav file_key, &host_line); 627ca3176e7SBrian Feldman if (host_status == HOST_NEW) { 628ca3176e7SBrian Feldman host_file = system_hostfile; 629af12a3e7SDag-Erling Smørgrav host_status = check_host_in_hostfile(host_file, host, host_key, 630af12a3e7SDag-Erling Smørgrav file_key, &host_line); 631ca3176e7SBrian Feldman } 632e8aafc91SKris Kennaway /* 633e8aafc91SKris Kennaway * Also perform check for the ip address, skip the check if we are 634e8aafc91SKris Kennaway * localhost or the hostname was an ip address to begin with 635e8aafc91SKris Kennaway */ 636ca3176e7SBrian Feldman if (options.check_host_ip) { 637e8aafc91SKris Kennaway Key *ip_key = key_new(host_key->type); 638e8aafc91SKris Kennaway 639ca3176e7SBrian Feldman ip_file = user_hostfile; 640af12a3e7SDag-Erling Smørgrav ip_status = check_host_in_hostfile(ip_file, ip, host_key, 641af12a3e7SDag-Erling Smørgrav ip_key, &ip_line); 642ca3176e7SBrian Feldman if (ip_status == HOST_NEW) { 643ca3176e7SBrian Feldman ip_file = system_hostfile; 644af12a3e7SDag-Erling Smørgrav ip_status = check_host_in_hostfile(ip_file, ip, 645af12a3e7SDag-Erling Smørgrav host_key, ip_key, &ip_line); 646ca3176e7SBrian Feldman } 647e8aafc91SKris Kennaway if (host_status == HOST_CHANGED && 648e8aafc91SKris Kennaway (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) 649e8aafc91SKris Kennaway host_ip_differ = 1; 650e8aafc91SKris Kennaway 651e8aafc91SKris Kennaway key_free(ip_key); 652e8aafc91SKris Kennaway } else 653e8aafc91SKris Kennaway ip_status = host_status; 654e8aafc91SKris Kennaway 655e8aafc91SKris Kennaway key_free(file_key); 656e8aafc91SKris Kennaway 657e8aafc91SKris Kennaway switch (host_status) { 658e8aafc91SKris Kennaway case HOST_OK: 659e8aafc91SKris Kennaway /* The host is known and the key matches. */ 660e8aafc91SKris Kennaway debug("Host '%.200s' is known and matches the %s host key.", 661e8aafc91SKris Kennaway host, type); 662ca3176e7SBrian Feldman debug("Found key in %s:%d", host_file, host_line); 663ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 664af12a3e7SDag-Erling Smørgrav if (readonly) 665cf2b5f3bSDag-Erling Smørgrav logit("%s host key for IP address " 666af12a3e7SDag-Erling Smørgrav "'%.128s' not in list of known hosts.", 667e8aafc91SKris Kennaway type, ip); 668af12a3e7SDag-Erling Smørgrav else if (!add_host_to_hostfile(user_hostfile, ip, 669aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts)) 670cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the %s host key for IP " 671af12a3e7SDag-Erling Smørgrav "address '%.128s' to the list of known " 672af12a3e7SDag-Erling Smørgrav "hosts (%.30s).", type, ip, user_hostfile); 673af12a3e7SDag-Erling Smørgrav else 674cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added the %s host " 675af12a3e7SDag-Erling Smørgrav "key for IP address '%.128s' to the list " 676af12a3e7SDag-Erling Smørgrav "of known hosts.", type, ip); 677e8aafc91SKris Kennaway } 678e8aafc91SKris Kennaway break; 679e8aafc91SKris Kennaway case HOST_NEW: 680333ee039SDag-Erling Smørgrav if (options.host_key_alias == NULL && port != 0 && 681333ee039SDag-Erling Smørgrav port != SSH_DEFAULT_PORT) { 682333ee039SDag-Erling Smørgrav debug("checking without port identifier"); 683333ee039SDag-Erling Smørgrav if (check_host_key(hostname, hostaddr, 0, host_key, 2, 684333ee039SDag-Erling Smørgrav user_hostfile, system_hostfile) == 0) { 685333ee039SDag-Erling Smørgrav debug("found matching key w/out port"); 686333ee039SDag-Erling Smørgrav break; 687333ee039SDag-Erling Smørgrav } 688333ee039SDag-Erling Smørgrav } 689af12a3e7SDag-Erling Smørgrav if (readonly) 690af12a3e7SDag-Erling Smørgrav goto fail; 691e8aafc91SKris Kennaway /* The host is new. */ 692e8aafc91SKris Kennaway if (options.strict_host_key_checking == 1) { 693af12a3e7SDag-Erling Smørgrav /* 694af12a3e7SDag-Erling Smørgrav * User has requested strict host key checking. We 695af12a3e7SDag-Erling Smørgrav * will not add the host key automatically. The only 696af12a3e7SDag-Erling Smørgrav * alternative left is to abort. 697af12a3e7SDag-Erling Smørgrav */ 698af12a3e7SDag-Erling Smørgrav error("No %s host key is known for %.200s and you " 699af12a3e7SDag-Erling Smørgrav "have requested strict checking.", type, host); 700af12a3e7SDag-Erling Smørgrav goto fail; 701e8aafc91SKris Kennaway } else if (options.strict_host_key_checking == 2) { 702cf2b5f3bSDag-Erling Smørgrav char msg1[1024], msg2[1024]; 703cf2b5f3bSDag-Erling Smørgrav 704cf2b5f3bSDag-Erling Smørgrav if (show_other_keys(host, host_key)) 705cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), 706cf2b5f3bSDag-Erling Smørgrav "\nbut keys of different type are already" 707cf2b5f3bSDag-Erling Smørgrav " known for this host."); 708cf2b5f3bSDag-Erling Smørgrav else 709cf2b5f3bSDag-Erling Smørgrav snprintf(msg1, sizeof(msg1), "."); 710e8aafc91SKris Kennaway /* The default */ 711ca3176e7SBrian Feldman fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 712cf2b5f3bSDag-Erling Smørgrav msg2[0] = '\0'; 713cf2b5f3bSDag-Erling Smørgrav if (options.verify_host_key_dns) { 7141ec0d754SDag-Erling Smørgrav if (matching_host_key_dns) 715cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 716cf2b5f3bSDag-Erling Smørgrav "Matching host key fingerprint" 717cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 718cf2b5f3bSDag-Erling Smørgrav else 719cf2b5f3bSDag-Erling Smørgrav snprintf(msg2, sizeof(msg2), 720cf2b5f3bSDag-Erling Smørgrav "No matching host key fingerprint" 721cf2b5f3bSDag-Erling Smørgrav " found in DNS.\n"); 722cf2b5f3bSDag-Erling Smørgrav } 723af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 724af12a3e7SDag-Erling Smørgrav "The authenticity of host '%.200s (%s)' can't be " 725f388f5efSDag-Erling Smørgrav "established%s\n" 726cf2b5f3bSDag-Erling Smørgrav "%s key fingerprint is %s.\n%s" 727af12a3e7SDag-Erling Smørgrav "Are you sure you want to continue connecting " 728f388f5efSDag-Erling Smørgrav "(yes/no)? ", 729cf2b5f3bSDag-Erling Smørgrav host, ip, msg1, type, fp, msg2); 730ca3176e7SBrian Feldman xfree(fp); 731af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 732af12a3e7SDag-Erling Smørgrav goto fail; 733e8aafc91SKris Kennaway } 734af12a3e7SDag-Erling Smørgrav /* 735af12a3e7SDag-Erling Smørgrav * If not in strict mode, add the key automatically to the 736af12a3e7SDag-Erling Smørgrav * local known_hosts file. 737af12a3e7SDag-Erling Smørgrav */ 738aa49c926SDag-Erling Smørgrav if (options.check_host_ip && ip_status == HOST_NEW) { 739aa49c926SDag-Erling Smørgrav snprintf(hostline, sizeof(hostline), "%s,%s", 740aa49c926SDag-Erling Smørgrav host, ip); 741aa49c926SDag-Erling Smørgrav hostp = hostline; 742aa49c926SDag-Erling Smørgrav if (options.hash_known_hosts) { 743aa49c926SDag-Erling Smørgrav /* Add hash of host and IP separately */ 744aa49c926SDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfile, host, 745aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts) && 746aa49c926SDag-Erling Smørgrav add_host_to_hostfile(user_hostfile, ip, 747aa49c926SDag-Erling Smørgrav host_key, options.hash_known_hosts); 748aa49c926SDag-Erling Smørgrav } else { 749aa49c926SDag-Erling Smørgrav /* Add unhashed "host,ip" */ 750aa49c926SDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfile, 751aa49c926SDag-Erling Smørgrav hostline, host_key, 752aa49c926SDag-Erling Smørgrav options.hash_known_hosts); 753aa49c926SDag-Erling Smørgrav } 754aa49c926SDag-Erling Smørgrav } else { 755aa49c926SDag-Erling Smørgrav r = add_host_to_hostfile(user_hostfile, host, host_key, 756aa49c926SDag-Erling Smørgrav options.hash_known_hosts); 757aa49c926SDag-Erling Smørgrav hostp = host; 758aa49c926SDag-Erling Smørgrav } 759aa49c926SDag-Erling Smørgrav 760aa49c926SDag-Erling Smørgrav if (!r) 761cf2b5f3bSDag-Erling Smørgrav logit("Failed to add the host to the list of known " 762af12a3e7SDag-Erling Smørgrav "hosts (%.500s).", user_hostfile); 763e8aafc91SKris Kennaway else 764cf2b5f3bSDag-Erling Smørgrav logit("Warning: Permanently added '%.200s' (%s) to the " 765af12a3e7SDag-Erling Smørgrav "list of known hosts.", hostp, type); 766e8aafc91SKris Kennaway break; 767e8aafc91SKris Kennaway case HOST_CHANGED: 768333ee039SDag-Erling Smørgrav if (readonly == ROQUIET) 769333ee039SDag-Erling Smørgrav goto fail; 770e8aafc91SKris Kennaway if (options.check_host_ip && host_ip_differ) { 77121e764dfSDag-Erling Smørgrav char *key_msg; 772e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 77321e764dfSDag-Erling Smørgrav key_msg = "is unknown"; 774e8aafc91SKris Kennaway else if (ip_status == HOST_OK) 77521e764dfSDag-Erling Smørgrav key_msg = "is unchanged"; 776e8aafc91SKris Kennaway else 77721e764dfSDag-Erling Smørgrav key_msg = "has a different value"; 778e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 779e8aafc91SKris Kennaway error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 780e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 781e8aafc91SKris Kennaway error("The %s host key for %s has changed,", type, host); 782e8aafc91SKris Kennaway error("and the key for the according IP address %s", ip); 78321e764dfSDag-Erling Smørgrav error("%s. This could either mean that", key_msg); 784e8aafc91SKris Kennaway error("DNS SPOOFING is happening or the IP address for the host"); 785ca3176e7SBrian Feldman error("and its host key have changed at the same time."); 786ca3176e7SBrian Feldman if (ip_status != HOST_NEW) 787ca3176e7SBrian Feldman error("Offending key for IP in %s:%d", ip_file, ip_line); 788e8aafc91SKris Kennaway } 789e8aafc91SKris Kennaway /* The host key has changed. */ 7901ec0d754SDag-Erling Smørgrav warn_changed_key(host_key); 791e8aafc91SKris Kennaway error("Add correct host key in %.100s to get rid of this message.", 792e8aafc91SKris Kennaway user_hostfile); 793ca3176e7SBrian Feldman error("Offending key in %s:%d", host_file, host_line); 794e8aafc91SKris Kennaway 795e8aafc91SKris Kennaway /* 796e8aafc91SKris Kennaway * If strict host key checking is in use, the user will have 797e8aafc91SKris Kennaway * to edit the key manually and we can only abort. 798e8aafc91SKris Kennaway */ 799af12a3e7SDag-Erling Smørgrav if (options.strict_host_key_checking) { 800af12a3e7SDag-Erling Smørgrav error("%s host key for %.200s has changed and you have " 801af12a3e7SDag-Erling Smørgrav "requested strict checking.", type, host); 802af12a3e7SDag-Erling Smørgrav goto fail; 803af12a3e7SDag-Erling Smørgrav } 804e8aafc91SKris Kennaway 805e8aafc91SKris Kennaway /* 806e8aafc91SKris Kennaway * If strict host key checking has not been requested, allow 807cf2b5f3bSDag-Erling Smørgrav * the connection but without MITM-able authentication or 808333ee039SDag-Erling Smørgrav * forwarding. 809e8aafc91SKris Kennaway */ 810e8aafc91SKris Kennaway if (options.password_authentication) { 811af12a3e7SDag-Erling Smørgrav error("Password authentication is disabled to avoid " 812af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 813e8aafc91SKris Kennaway options.password_authentication = 0; 814e8aafc91SKris Kennaway } 815cf2b5f3bSDag-Erling Smørgrav if (options.kbd_interactive_authentication) { 816cf2b5f3bSDag-Erling Smørgrav error("Keyboard-interactive authentication is disabled" 817cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 818cf2b5f3bSDag-Erling Smørgrav options.kbd_interactive_authentication = 0; 819cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 820cf2b5f3bSDag-Erling Smørgrav } 821cf2b5f3bSDag-Erling Smørgrav if (options.challenge_response_authentication) { 822cf2b5f3bSDag-Erling Smørgrav error("Challenge/response authentication is disabled" 823cf2b5f3bSDag-Erling Smørgrav " to avoid man-in-the-middle attacks."); 824cf2b5f3bSDag-Erling Smørgrav options.challenge_response_authentication = 0; 825cf2b5f3bSDag-Erling Smørgrav } 826e8aafc91SKris Kennaway if (options.forward_agent) { 827af12a3e7SDag-Erling Smørgrav error("Agent forwarding is disabled to avoid " 828af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 829e8aafc91SKris Kennaway options.forward_agent = 0; 830e8aafc91SKris Kennaway } 831ca3176e7SBrian Feldman if (options.forward_x11) { 832af12a3e7SDag-Erling Smørgrav error("X11 forwarding is disabled to avoid " 833af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 834ca3176e7SBrian Feldman options.forward_x11 = 0; 835ca3176e7SBrian Feldman } 836af12a3e7SDag-Erling Smørgrav if (options.num_local_forwards > 0 || 837af12a3e7SDag-Erling Smørgrav options.num_remote_forwards > 0) { 838af12a3e7SDag-Erling Smørgrav error("Port forwarding is disabled to avoid " 839af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 840af12a3e7SDag-Erling Smørgrav options.num_local_forwards = 841af12a3e7SDag-Erling Smørgrav options.num_remote_forwards = 0; 842ca3176e7SBrian Feldman } 843333ee039SDag-Erling Smørgrav if (options.tun_open != SSH_TUNMODE_NO) { 844333ee039SDag-Erling Smørgrav error("Tunnel forwarding is disabled to avoid " 845333ee039SDag-Erling Smørgrav "man-in-the-middle attacks."); 846333ee039SDag-Erling Smørgrav options.tun_open = SSH_TUNMODE_NO; 847333ee039SDag-Erling Smørgrav } 848e8aafc91SKris Kennaway /* 849e8aafc91SKris Kennaway * XXX Should permit the user to change to use the new id. 850e8aafc91SKris Kennaway * This could be done by converting the host key to an 851e8aafc91SKris Kennaway * identifying sentence, tell that the host identifies itself 852e8aafc91SKris Kennaway * by that sentence, and ask the user if he/she whishes to 853e8aafc91SKris Kennaway * accept the authentication. 854e8aafc91SKris Kennaway */ 855e8aafc91SKris Kennaway break; 856f388f5efSDag-Erling Smørgrav case HOST_FOUND: 857f388f5efSDag-Erling Smørgrav fatal("internal error"); 858f388f5efSDag-Erling Smørgrav break; 859e8aafc91SKris Kennaway } 860ca3176e7SBrian Feldman 861ca3176e7SBrian Feldman if (options.check_host_ip && host_status != HOST_CHANGED && 862ca3176e7SBrian Feldman ip_status == HOST_CHANGED) { 863af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 864af12a3e7SDag-Erling Smørgrav "Warning: the %s host key for '%.200s' " 865af12a3e7SDag-Erling Smørgrav "differs from the key for the IP address '%.128s'" 866af12a3e7SDag-Erling Smørgrav "\nOffending key for IP in %s:%d", 867af12a3e7SDag-Erling Smørgrav type, host, ip, ip_file, ip_line); 868af12a3e7SDag-Erling Smørgrav if (host_status == HOST_OK) { 869af12a3e7SDag-Erling Smørgrav len = strlen(msg); 870af12a3e7SDag-Erling Smørgrav snprintf(msg + len, sizeof(msg) - len, 871af12a3e7SDag-Erling Smørgrav "\nMatching host key in %s:%d", 872af12a3e7SDag-Erling Smørgrav host_file, host_line); 873af12a3e7SDag-Erling Smørgrav } 874ca3176e7SBrian Feldman if (options.strict_host_key_checking == 1) { 875cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 876af12a3e7SDag-Erling Smørgrav error("Exiting, you have requested strict checking."); 877af12a3e7SDag-Erling Smørgrav goto fail; 878ca3176e7SBrian Feldman } else if (options.strict_host_key_checking == 2) { 879af12a3e7SDag-Erling Smørgrav strlcat(msg, "\nAre you sure you want " 880af12a3e7SDag-Erling Smørgrav "to continue connecting (yes/no)? ", sizeof(msg)); 881af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 882af12a3e7SDag-Erling Smørgrav goto fail; 883af12a3e7SDag-Erling Smørgrav } else { 884cf2b5f3bSDag-Erling Smørgrav logit("%s", msg); 885ca3176e7SBrian Feldman } 886ca3176e7SBrian Feldman } 887ca3176e7SBrian Feldman 888e8aafc91SKris Kennaway xfree(ip); 889333ee039SDag-Erling Smørgrav xfree(host); 890af12a3e7SDag-Erling Smørgrav return 0; 891af12a3e7SDag-Erling Smørgrav 892af12a3e7SDag-Erling Smørgrav fail: 893af12a3e7SDag-Erling Smørgrav xfree(ip); 894333ee039SDag-Erling Smørgrav xfree(host); 895af12a3e7SDag-Erling Smørgrav return -1; 896e8aafc91SKris Kennaway } 897511b41d2SMark Murray 898cf2b5f3bSDag-Erling Smørgrav /* returns 0 if key verifies or -1 if key does NOT verify */ 899fe5fd017SMark Murray int 900af12a3e7SDag-Erling Smørgrav verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 901fe5fd017SMark Murray { 902af12a3e7SDag-Erling Smørgrav struct stat st; 9031ec0d754SDag-Erling Smørgrav int flags = 0; 904fe5fd017SMark Murray 9051ec0d754SDag-Erling Smørgrav if (options.verify_host_key_dns && 9061ec0d754SDag-Erling Smørgrav verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { 9071ec0d754SDag-Erling Smørgrav 9081ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_FOUND) { 9091ec0d754SDag-Erling Smørgrav 9101ec0d754SDag-Erling Smørgrav if (options.verify_host_key_dns == 1 && 9111ec0d754SDag-Erling Smørgrav flags & DNS_VERIFY_MATCH && 9121ec0d754SDag-Erling Smørgrav flags & DNS_VERIFY_SECURE) 913cf2b5f3bSDag-Erling Smørgrav return 0; 9141ec0d754SDag-Erling Smørgrav 9151ec0d754SDag-Erling Smørgrav if (flags & DNS_VERIFY_MATCH) { 9161ec0d754SDag-Erling Smørgrav matching_host_key_dns = 1; 9171ec0d754SDag-Erling Smørgrav } else { 9181ec0d754SDag-Erling Smørgrav warn_changed_key(host_key); 9191ec0d754SDag-Erling Smørgrav error("Update the SSHFP RR in DNS with the new " 9201ec0d754SDag-Erling Smørgrav "host key to get rid of this message."); 921cf2b5f3bSDag-Erling Smørgrav } 922cf2b5f3bSDag-Erling Smørgrav } 9231ec0d754SDag-Erling Smørgrav } 924cf2b5f3bSDag-Erling Smørgrav 925af12a3e7SDag-Erling Smørgrav /* return ok if the key can be found in an old keyfile */ 926af12a3e7SDag-Erling Smørgrav if (stat(options.system_hostfile2, &st) == 0 || 927af12a3e7SDag-Erling Smørgrav stat(options.user_hostfile2, &st) == 0) { 928333ee039SDag-Erling Smørgrav if (check_host_key(host, hostaddr, options.port, host_key, 929333ee039SDag-Erling Smørgrav RDONLY, options.user_hostfile2, 930333ee039SDag-Erling Smørgrav options.system_hostfile2) == 0) 931af12a3e7SDag-Erling Smørgrav return 0; 932fe5fd017SMark Murray } 933333ee039SDag-Erling Smørgrav return check_host_key(host, hostaddr, options.port, host_key, 934333ee039SDag-Erling Smørgrav RDRW, options.user_hostfile, options.system_hostfile); 935fe5fd017SMark Murray } 936fe5fd017SMark Murray 937511b41d2SMark Murray /* 938511b41d2SMark Murray * Starts a dialog with the server, and authenticates the current user on the 939511b41d2SMark Murray * server. This does not need any extra privileges. The basic connection 940511b41d2SMark Murray * to the server must already have been established before this is called. 941511b41d2SMark Murray * If login fails, this function prints an error and never returns. 942511b41d2SMark Murray * This function does not require super-user privileges. 943511b41d2SMark Murray */ 944511b41d2SMark Murray void 94580628bacSDag-Erling Smørgrav ssh_login(Sensitive *sensitive, const char *orighost, 946ca3176e7SBrian Feldman struct sockaddr *hostaddr, struct passwd *pw) 947511b41d2SMark Murray { 948511b41d2SMark Murray char *host, *cp; 949e8aafc91SKris Kennaway char *server_user, *local_user; 950e8aafc91SKris Kennaway 951e8aafc91SKris Kennaway local_user = xstrdup(pw->pw_name); 952e8aafc91SKris Kennaway server_user = options.user ? options.user : local_user; 953511b41d2SMark Murray 954511b41d2SMark Murray /* Convert the user-supplied hostname into all lowercase. */ 955511b41d2SMark Murray host = xstrdup(orighost); 956511b41d2SMark Murray for (cp = host; *cp; cp++) 957511b41d2SMark Murray if (isupper(*cp)) 958333ee039SDag-Erling Smørgrav *cp = (char)tolower(*cp); 959511b41d2SMark Murray 960511b41d2SMark Murray /* Exchange protocol version identification strings with the server. */ 961511b41d2SMark Murray ssh_exchange_identification(); 962511b41d2SMark Murray 963511b41d2SMark Murray /* Put the connection into non-blocking mode. */ 964511b41d2SMark Murray packet_set_nonblocking(); 965511b41d2SMark Murray 966511b41d2SMark Murray /* key exchange */ 967511b41d2SMark Murray /* authenticate user */ 968e8aafc91SKris Kennaway if (compat20) { 969e8aafc91SKris Kennaway ssh_kex2(host, hostaddr); 97080628bacSDag-Erling Smørgrav ssh_userauth2(local_user, server_user, host, sensitive); 971e8aafc91SKris Kennaway } else { 972e8aafc91SKris Kennaway ssh_kex(host, hostaddr); 97380628bacSDag-Erling Smørgrav ssh_userauth1(local_user, server_user, host, sensitive); 974e8aafc91SKris Kennaway } 975333ee039SDag-Erling Smørgrav xfree(local_user); 976511b41d2SMark Murray } 977e0fbb1d2SBrian Feldman 978e0fbb1d2SBrian Feldman void 979e0fbb1d2SBrian Feldman ssh_put_password(char *password) 980e0fbb1d2SBrian Feldman { 981e0fbb1d2SBrian Feldman int size; 982e0fbb1d2SBrian Feldman char *padded; 983e0fbb1d2SBrian Feldman 984ca3176e7SBrian Feldman if (datafellows & SSH_BUG_PASSWORDPAD) { 985af12a3e7SDag-Erling Smørgrav packet_put_cstring(password); 986ca3176e7SBrian Feldman return; 987ca3176e7SBrian Feldman } 988e0fbb1d2SBrian Feldman size = roundup(strlen(password) + 1, 32); 989333ee039SDag-Erling Smørgrav padded = xcalloc(1, size); 990e0fbb1d2SBrian Feldman strlcpy(padded, password, size); 991e0fbb1d2SBrian Feldman packet_put_string(padded, size); 992e0fbb1d2SBrian Feldman memset(padded, 0, size); 993e0fbb1d2SBrian Feldman xfree(padded); 994e0fbb1d2SBrian Feldman } 995f388f5efSDag-Erling Smørgrav 996f388f5efSDag-Erling Smørgrav static int 997f388f5efSDag-Erling Smørgrav show_key_from_file(const char *file, const char *host, int keytype) 998f388f5efSDag-Erling Smørgrav { 999f388f5efSDag-Erling Smørgrav Key *found; 1000f388f5efSDag-Erling Smørgrav char *fp; 1001f388f5efSDag-Erling Smørgrav int line, ret; 1002f388f5efSDag-Erling Smørgrav 1003f388f5efSDag-Erling Smørgrav found = key_new(keytype); 1004f388f5efSDag-Erling Smørgrav if ((ret = lookup_key_in_hostfile_by_type(file, host, 1005f388f5efSDag-Erling Smørgrav keytype, found, &line))) { 1006f388f5efSDag-Erling Smørgrav fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); 1007cf2b5f3bSDag-Erling Smørgrav logit("WARNING: %s key found for host %s\n" 1008f388f5efSDag-Erling Smørgrav "in %s:%d\n" 1009f388f5efSDag-Erling Smørgrav "%s key fingerprint %s.", 1010f388f5efSDag-Erling Smørgrav key_type(found), host, file, line, 1011f388f5efSDag-Erling Smørgrav key_type(found), fp); 1012f388f5efSDag-Erling Smørgrav xfree(fp); 1013f388f5efSDag-Erling Smørgrav } 1014f388f5efSDag-Erling Smørgrav key_free(found); 1015f388f5efSDag-Erling Smørgrav return (ret); 1016f388f5efSDag-Erling Smørgrav } 1017f388f5efSDag-Erling Smørgrav 1018f388f5efSDag-Erling Smørgrav /* print all known host keys for a given host, but skip keys of given type */ 1019f388f5efSDag-Erling Smørgrav static int 1020f388f5efSDag-Erling Smørgrav show_other_keys(const char *host, Key *key) 1021f388f5efSDag-Erling Smørgrav { 1022f388f5efSDag-Erling Smørgrav int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, -1}; 1023f388f5efSDag-Erling Smørgrav int i, found = 0; 1024f388f5efSDag-Erling Smørgrav 1025f388f5efSDag-Erling Smørgrav for (i = 0; type[i] != -1; i++) { 1026f388f5efSDag-Erling Smørgrav if (type[i] == key->type) 1027f388f5efSDag-Erling Smørgrav continue; 1028f388f5efSDag-Erling Smørgrav if (type[i] != KEY_RSA1 && 1029f388f5efSDag-Erling Smørgrav show_key_from_file(options.user_hostfile2, host, type[i])) { 1030f388f5efSDag-Erling Smørgrav found = 1; 1031f388f5efSDag-Erling Smørgrav continue; 1032f388f5efSDag-Erling Smørgrav } 1033f388f5efSDag-Erling Smørgrav if (type[i] != KEY_RSA1 && 1034f388f5efSDag-Erling Smørgrav show_key_from_file(options.system_hostfile2, host, type[i])) { 1035f388f5efSDag-Erling Smørgrav found = 1; 1036f388f5efSDag-Erling Smørgrav continue; 1037f388f5efSDag-Erling Smørgrav } 1038f388f5efSDag-Erling Smørgrav if (show_key_from_file(options.user_hostfile, host, type[i])) { 1039f388f5efSDag-Erling Smørgrav found = 1; 1040f388f5efSDag-Erling Smørgrav continue; 1041f388f5efSDag-Erling Smørgrav } 1042f388f5efSDag-Erling Smørgrav if (show_key_from_file(options.system_hostfile, host, type[i])) { 1043f388f5efSDag-Erling Smørgrav found = 1; 1044f388f5efSDag-Erling Smørgrav continue; 1045f388f5efSDag-Erling Smørgrav } 1046f388f5efSDag-Erling Smørgrav debug2("no key of type %d for host %s", type[i], host); 1047f388f5efSDag-Erling Smørgrav } 1048f388f5efSDag-Erling Smørgrav return (found); 1049f388f5efSDag-Erling Smørgrav } 10501ec0d754SDag-Erling Smørgrav 10511ec0d754SDag-Erling Smørgrav static void 10521ec0d754SDag-Erling Smørgrav warn_changed_key(Key *host_key) 10531ec0d754SDag-Erling Smørgrav { 10541ec0d754SDag-Erling Smørgrav char *fp; 10551ec0d754SDag-Erling Smørgrav const char *type = key_type(host_key); 10561ec0d754SDag-Erling Smørgrav 10571ec0d754SDag-Erling Smørgrav fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 10581ec0d754SDag-Erling Smørgrav 10591ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 10601ec0d754SDag-Erling Smørgrav error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 10611ec0d754SDag-Erling Smørgrav error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 10621ec0d754SDag-Erling Smørgrav error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 10631ec0d754SDag-Erling Smørgrav error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 10641ec0d754SDag-Erling Smørgrav error("It is also possible that the %s host key has just been changed.", type); 10651ec0d754SDag-Erling Smørgrav error("The fingerprint for the %s key sent by the remote host is\n%s.", 10661ec0d754SDag-Erling Smørgrav type, fp); 10671ec0d754SDag-Erling Smørgrav error("Please contact your system administrator."); 10681ec0d754SDag-Erling Smørgrav 10691ec0d754SDag-Erling Smørgrav xfree(fp); 10701ec0d754SDag-Erling Smørgrav } 1071b74df5b2SDag-Erling Smørgrav 1072b74df5b2SDag-Erling Smørgrav /* 1073b74df5b2SDag-Erling Smørgrav * Execute a local command 1074b74df5b2SDag-Erling Smørgrav */ 1075b74df5b2SDag-Erling Smørgrav int 1076b74df5b2SDag-Erling Smørgrav ssh_local_cmd(const char *args) 1077b74df5b2SDag-Erling Smørgrav { 1078b74df5b2SDag-Erling Smørgrav char *shell; 1079b74df5b2SDag-Erling Smørgrav pid_t pid; 1080b74df5b2SDag-Erling Smørgrav int status; 1081b74df5b2SDag-Erling Smørgrav 1082b74df5b2SDag-Erling Smørgrav if (!options.permit_local_command || 1083b74df5b2SDag-Erling Smørgrav args == NULL || !*args) 1084b74df5b2SDag-Erling Smørgrav return (1); 1085b74df5b2SDag-Erling Smørgrav 1086b74df5b2SDag-Erling Smørgrav if ((shell = getenv("SHELL")) == NULL) 1087b74df5b2SDag-Erling Smørgrav shell = _PATH_BSHELL; 1088b74df5b2SDag-Erling Smørgrav 1089b74df5b2SDag-Erling Smørgrav pid = fork(); 1090b74df5b2SDag-Erling Smørgrav if (pid == 0) { 1091b74df5b2SDag-Erling Smørgrav debug3("Executing %s -c \"%s\"", shell, args); 1092b74df5b2SDag-Erling Smørgrav execl(shell, shell, "-c", args, (char *)NULL); 1093b74df5b2SDag-Erling Smørgrav error("Couldn't execute %s -c \"%s\": %s", 1094b74df5b2SDag-Erling Smørgrav shell, args, strerror(errno)); 1095b74df5b2SDag-Erling Smørgrav _exit(1); 1096b74df5b2SDag-Erling Smørgrav } else if (pid == -1) 1097b74df5b2SDag-Erling Smørgrav fatal("fork failed: %.100s", strerror(errno)); 1098b74df5b2SDag-Erling Smørgrav while (waitpid(pid, &status, 0) == -1) 1099b74df5b2SDag-Erling Smørgrav if (errno != EINTR) 1100b74df5b2SDag-Erling Smørgrav fatal("Couldn't wait for child: %s", strerror(errno)); 1101b74df5b2SDag-Erling Smørgrav 1102b74df5b2SDag-Erling Smørgrav if (!WIFEXITED(status)) 1103b74df5b2SDag-Erling Smørgrav return (1); 1104b74df5b2SDag-Erling Smørgrav 1105b74df5b2SDag-Erling Smørgrav return (WEXITSTATUS(status)); 1106b74df5b2SDag-Erling Smørgrav } 1107