1511b41d2SMark Murray /* 2511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 3511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4511b41d2SMark Murray * All rights reserved 5511b41d2SMark Murray * Code to connect to a remote host, and to perform the client side of the 6511b41d2SMark Murray * login (authentication) dialog. 742f71286SMark Murray * 8c2d3a559SKris Kennaway * As far as I am concerned, the code I have written for this software 9c2d3a559SKris Kennaway * can be used freely for any purpose. Any derived versions of this 10c2d3a559SKris Kennaway * software must be clearly marked as such, and if the derived work is 11c2d3a559SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 12c2d3a559SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 13511b41d2SMark Murray */ 14511b41d2SMark Murray 15511b41d2SMark Murray #include "includes.h" 16af12a3e7SDag-Erling Smørgrav RCSID("$OpenBSD: sshconnect.c,v 1.119 2002/01/21 15:13:51 markus Exp $"); 17c2d3a559SKris Kennaway RCSID("$FreeBSD$"); 18511b41d2SMark Murray 1918a71195SBrian Feldman #include <openssl/bn.h> 20e8aafc91SKris Kennaway 21ca3176e7SBrian Feldman #include "ssh.h" 22511b41d2SMark Murray #include "xmalloc.h" 23511b41d2SMark Murray #include "rsa.h" 24e8aafc91SKris Kennaway #include "buffer.h" 25511b41d2SMark Murray #include "packet.h" 26511b41d2SMark Murray #include "uidswap.h" 27511b41d2SMark Murray #include "compat.h" 283c6ae118SKris Kennaway #include "key.h" 29e8aafc91SKris Kennaway #include "sshconnect.h" 303c6ae118SKris Kennaway #include "hostfile.h" 31ca3176e7SBrian Feldman #include "log.h" 32ca3176e7SBrian Feldman #include "readconf.h" 33ca3176e7SBrian Feldman #include "atomicio.h" 34ca3176e7SBrian Feldman #include "misc.h" 35af12a3e7SDag-Erling Smørgrav #include "readpass.h" 36511b41d2SMark Murray 37e8aafc91SKris Kennaway char *client_version_string = NULL; 38e8aafc91SKris Kennaway char *server_version_string = NULL; 39511b41d2SMark Murray 40511b41d2SMark Murray extern Options options; 41511b41d2SMark Murray extern char *__progname; 42511b41d2SMark Murray 43af12a3e7SDag-Erling Smørgrav static const char * 44af12a3e7SDag-Erling Smørgrav sockaddr_ntop(struct sockaddr *sa) 45af12a3e7SDag-Erling Smørgrav { 46af12a3e7SDag-Erling Smørgrav void *addr; 47af12a3e7SDag-Erling Smørgrav static char addrbuf[INET6_ADDRSTRLEN]; 48af12a3e7SDag-Erling Smørgrav 49af12a3e7SDag-Erling Smørgrav switch (sa->sa_family) { 50af12a3e7SDag-Erling Smørgrav case AF_INET: 51af12a3e7SDag-Erling Smørgrav addr = &((struct sockaddr_in *)sa)->sin_addr; 52af12a3e7SDag-Erling Smørgrav break; 53af12a3e7SDag-Erling Smørgrav case AF_INET6: 54af12a3e7SDag-Erling Smørgrav addr = &((struct sockaddr_in6 *)sa)->sin6_addr; 55af12a3e7SDag-Erling Smørgrav break; 56af12a3e7SDag-Erling Smørgrav default: 57af12a3e7SDag-Erling Smørgrav /* This case should be protected against elsewhere */ 58af12a3e7SDag-Erling Smørgrav abort(); /* XXX abort is bad -- do something else */ 59af12a3e7SDag-Erling Smørgrav } 60af12a3e7SDag-Erling Smørgrav inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf)); 61af12a3e7SDag-Erling Smørgrav return addrbuf; 62af12a3e7SDag-Erling Smørgrav } 63ca3176e7SBrian Feldman 64511b41d2SMark Murray /* 65511b41d2SMark Murray * Connect to the given ssh server using a proxy command. 66511b41d2SMark Murray */ 67af12a3e7SDag-Erling Smørgrav static int 68ca3176e7SBrian Feldman ssh_proxy_connect(const char *host, u_short port, struct passwd *pw, 69511b41d2SMark Murray const char *proxy_command) 70511b41d2SMark Murray { 71511b41d2SMark Murray Buffer command; 72511b41d2SMark Murray const char *cp; 73511b41d2SMark Murray char *command_string; 74511b41d2SMark Murray int pin[2], pout[2]; 75e8aafc91SKris Kennaway pid_t pid; 76511b41d2SMark Murray char strport[NI_MAXSERV]; 77511b41d2SMark Murray 78511b41d2SMark Murray /* Convert the port number into a string. */ 79511b41d2SMark Murray snprintf(strport, sizeof strport, "%hu", port); 80511b41d2SMark Murray 81511b41d2SMark Murray /* Build the final command string in the buffer by making the 82511b41d2SMark Murray appropriate substitutions to the given proxy command. */ 83511b41d2SMark Murray buffer_init(&command); 84511b41d2SMark Murray for (cp = proxy_command; *cp; cp++) { 85511b41d2SMark Murray if (cp[0] == '%' && cp[1] == '%') { 86511b41d2SMark Murray buffer_append(&command, "%", 1); 87511b41d2SMark Murray cp++; 88511b41d2SMark Murray continue; 89511b41d2SMark Murray } 90511b41d2SMark Murray if (cp[0] == '%' && cp[1] == 'h') { 91511b41d2SMark Murray buffer_append(&command, host, strlen(host)); 92511b41d2SMark Murray cp++; 93511b41d2SMark Murray continue; 94511b41d2SMark Murray } 95511b41d2SMark Murray if (cp[0] == '%' && cp[1] == 'p') { 96511b41d2SMark Murray buffer_append(&command, strport, strlen(strport)); 97511b41d2SMark Murray cp++; 98511b41d2SMark Murray continue; 99511b41d2SMark Murray } 100511b41d2SMark Murray buffer_append(&command, cp, 1); 101511b41d2SMark Murray } 102511b41d2SMark Murray buffer_append(&command, "\0", 1); 103511b41d2SMark Murray 104511b41d2SMark Murray /* Get the final command string. */ 105511b41d2SMark Murray command_string = buffer_ptr(&command); 106511b41d2SMark Murray 107511b41d2SMark Murray /* Create pipes for communicating with the proxy. */ 108511b41d2SMark Murray if (pipe(pin) < 0 || pipe(pout) < 0) 109511b41d2SMark Murray fatal("Could not create pipes to communicate with the proxy: %.100s", 110511b41d2SMark Murray strerror(errno)); 111511b41d2SMark Murray 112511b41d2SMark Murray debug("Executing proxy command: %.500s", command_string); 113511b41d2SMark Murray 114511b41d2SMark Murray /* Fork and execute the proxy command. */ 115511b41d2SMark Murray if ((pid = fork()) == 0) { 116511b41d2SMark Murray char *argv[10]; 117511b41d2SMark Murray 118511b41d2SMark Murray /* Child. Permanently give up superuser privileges. */ 119ca3176e7SBrian Feldman permanently_set_uid(pw); 120511b41d2SMark Murray 121511b41d2SMark Murray /* Redirect stdin and stdout. */ 122511b41d2SMark Murray close(pin[1]); 123511b41d2SMark Murray if (pin[0] != 0) { 124511b41d2SMark Murray if (dup2(pin[0], 0) < 0) 125511b41d2SMark Murray perror("dup2 stdin"); 126511b41d2SMark Murray close(pin[0]); 127511b41d2SMark Murray } 128511b41d2SMark Murray close(pout[0]); 129511b41d2SMark Murray if (dup2(pout[1], 1) < 0) 130511b41d2SMark Murray perror("dup2 stdout"); 131511b41d2SMark Murray /* Cannot be 1 because pin allocated two descriptors. */ 132511b41d2SMark Murray close(pout[1]); 133511b41d2SMark Murray 134511b41d2SMark Murray /* Stderr is left as it is so that error messages get 135511b41d2SMark Murray printed on the user's terminal. */ 136ca3176e7SBrian Feldman argv[0] = _PATH_BSHELL; 137511b41d2SMark Murray argv[1] = "-c"; 138511b41d2SMark Murray argv[2] = command_string; 139511b41d2SMark Murray argv[3] = NULL; 140511b41d2SMark Murray 141511b41d2SMark Murray /* Execute the proxy command. Note that we gave up any 142511b41d2SMark Murray extra privileges above. */ 143ca3176e7SBrian Feldman execv(argv[0], argv); 144ca3176e7SBrian Feldman perror(argv[0]); 145511b41d2SMark Murray exit(1); 146511b41d2SMark Murray } 147511b41d2SMark Murray /* Parent. */ 148511b41d2SMark Murray if (pid < 0) 149511b41d2SMark Murray fatal("fork failed: %.100s", strerror(errno)); 150511b41d2SMark Murray 151511b41d2SMark Murray /* Close child side of the descriptors. */ 152511b41d2SMark Murray close(pin[0]); 153511b41d2SMark Murray close(pout[1]); 154511b41d2SMark Murray 155511b41d2SMark Murray /* Free the command name. */ 156511b41d2SMark Murray buffer_free(&command); 157511b41d2SMark Murray 158511b41d2SMark Murray /* Set the connection file descriptors. */ 159511b41d2SMark Murray packet_set_connection(pout[0], pin[1]); 160511b41d2SMark Murray 161af12a3e7SDag-Erling Smørgrav /* Indicate OK return */ 162af12a3e7SDag-Erling Smørgrav return 0; 163511b41d2SMark Murray } 164511b41d2SMark Murray 165511b41d2SMark Murray /* 166511b41d2SMark Murray * Creates a (possibly privileged) socket for use as the ssh connection. 167511b41d2SMark Murray */ 168af12a3e7SDag-Erling Smørgrav static int 169ca3176e7SBrian Feldman ssh_create_socket(struct passwd *pw, int privileged, int family) 170511b41d2SMark Murray { 171af12a3e7SDag-Erling Smørgrav int sock, gaierr; 172af12a3e7SDag-Erling Smørgrav struct addrinfo hints, *res; 173511b41d2SMark Murray 174511b41d2SMark Murray /* 175511b41d2SMark Murray * If we are running as root and want to connect to a privileged 176511b41d2SMark Murray * port, bind our own socket to a privileged port. 177511b41d2SMark Murray */ 178511b41d2SMark Murray if (privileged) { 179511b41d2SMark Murray int p = IPPORT_RESERVED - 1; 180511b41d2SMark Murray sock = rresvport_af(&p, family); 181511b41d2SMark Murray if (sock < 0) 182511b41d2SMark Murray error("rresvport: af=%d %.100s", family, strerror(errno)); 183511b41d2SMark Murray else 184511b41d2SMark Murray debug("Allocated local port %d.", p); 185af12a3e7SDag-Erling Smørgrav return sock; 186af12a3e7SDag-Erling Smørgrav } 187511b41d2SMark Murray /* 188511b41d2SMark Murray * Just create an ordinary socket on arbitrary port. We use 189511b41d2SMark Murray * the user's uid to create the socket. 190511b41d2SMark Murray */ 191ca3176e7SBrian Feldman temporarily_use_uid(pw); 192511b41d2SMark Murray sock = socket(family, SOCK_STREAM, 0); 193511b41d2SMark Murray if (sock < 0) 194511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 195511b41d2SMark Murray restore_uid(); 196af12a3e7SDag-Erling Smørgrav 197af12a3e7SDag-Erling Smørgrav /* Bind the socket to an alternative local IP address */ 198af12a3e7SDag-Erling Smørgrav if (options.bind_address == NULL) 199af12a3e7SDag-Erling Smørgrav return sock; 200af12a3e7SDag-Erling Smørgrav 201af12a3e7SDag-Erling Smørgrav memset(&hints, 0, sizeof(hints)); 202af12a3e7SDag-Erling Smørgrav hints.ai_family = family; 203af12a3e7SDag-Erling Smørgrav hints.ai_socktype = SOCK_STREAM; 204af12a3e7SDag-Erling Smørgrav hints.ai_flags = AI_PASSIVE; 205af12a3e7SDag-Erling Smørgrav gaierr = getaddrinfo(options.bind_address, "0", &hints, &res); 206af12a3e7SDag-Erling Smørgrav if (gaierr) { 207af12a3e7SDag-Erling Smørgrav error("getaddrinfo: %s: %s", options.bind_address, 208af12a3e7SDag-Erling Smørgrav gai_strerror(gaierr)); 209af12a3e7SDag-Erling Smørgrav close(sock); 210af12a3e7SDag-Erling Smørgrav return -1; 211511b41d2SMark Murray } 212af12a3e7SDag-Erling Smørgrav if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { 213af12a3e7SDag-Erling Smørgrav error("bind: %s: %s", options.bind_address, strerror(errno)); 214af12a3e7SDag-Erling Smørgrav close(sock); 215af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 216af12a3e7SDag-Erling Smørgrav return -1; 217af12a3e7SDag-Erling Smørgrav } 218af12a3e7SDag-Erling Smørgrav freeaddrinfo(res); 219511b41d2SMark Murray return sock; 220511b41d2SMark Murray } 221511b41d2SMark Murray 222511b41d2SMark Murray /* 223511b41d2SMark Murray * Opens a TCP/IP connection to the remote server on the given host. 224511b41d2SMark Murray * The address of the remote host will be returned in hostaddr. 225511b41d2SMark Murray * If port is 0, the default port will be used. If anonymous is zero, 226511b41d2SMark Murray * a privileged port will be allocated to make the connection. 227511b41d2SMark Murray * This requires super-user privileges if anonymous is false. 228511b41d2SMark Murray * Connection_attempts specifies the maximum number of tries (one per 229511b41d2SMark Murray * second). If proxy_command is non-NULL, it specifies the command (with %h 230511b41d2SMark Murray * and %p substituted for host and port, respectively) to use to contact 231511b41d2SMark Murray * the daemon. 232af12a3e7SDag-Erling Smørgrav * Return values: 233af12a3e7SDag-Erling Smørgrav * 0 for OK 234af12a3e7SDag-Erling Smørgrav * ECONNREFUSED if we got a "Connection Refused" by the peer on any address 235af12a3e7SDag-Erling Smørgrav * ECONNABORTED if we failed without a "Connection refused" 236af12a3e7SDag-Erling Smørgrav * Suitable error messages for the connection failure will already have been 237af12a3e7SDag-Erling Smørgrav * printed. 238511b41d2SMark Murray */ 239511b41d2SMark Murray int 2401f5ce8f4SBrian Feldman ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 241af12a3e7SDag-Erling Smørgrav u_short port, int family, int connection_attempts, 242af12a3e7SDag-Erling Smørgrav int anonymous, struct passwd *pw, const char *proxy_command) 243511b41d2SMark Murray { 244511b41d2SMark Murray int gaierr; 245ca3176e7SBrian Feldman int on = 1; 246ca3176e7SBrian Feldman int sock = -1, attempt; 247ca3176e7SBrian Feldman char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 248ca3176e7SBrian Feldman struct addrinfo hints, *ai, *aitop; 249511b41d2SMark Murray struct linger linger; 250ca3176e7SBrian Feldman struct servent *sp; 251af12a3e7SDag-Erling Smørgrav /* 252af12a3e7SDag-Erling Smørgrav * Did we get only other errors than "Connection refused" (which 253af12a3e7SDag-Erling Smørgrav * should block fallback to rsh and similar), or did we get at least 254af12a3e7SDag-Erling Smørgrav * one "Connection refused"? 255af12a3e7SDag-Erling Smørgrav */ 256af12a3e7SDag-Erling Smørgrav int full_failure = 1; 257511b41d2SMark Murray 258c2d3a559SKris Kennaway debug("ssh_connect: getuid %u geteuid %u anon %d", 259c2d3a559SKris Kennaway (u_int) getuid(), (u_int) geteuid(), anonymous); 260511b41d2SMark Murray 261511b41d2SMark Murray /* Get default port if port has not been set. */ 262511b41d2SMark Murray if (port == 0) { 263511b41d2SMark Murray sp = getservbyname(SSH_SERVICE_NAME, "tcp"); 264511b41d2SMark Murray if (sp) 265511b41d2SMark Murray port = ntohs(sp->s_port); 266511b41d2SMark Murray else 267511b41d2SMark Murray port = SSH_DEFAULT_PORT; 268511b41d2SMark Murray } 269511b41d2SMark Murray /* If a proxy command is given, connect using it. */ 270511b41d2SMark Murray if (proxy_command != NULL) 271ca3176e7SBrian Feldman return ssh_proxy_connect(host, port, pw, proxy_command); 272511b41d2SMark Murray 273511b41d2SMark Murray /* No proxy command. */ 274511b41d2SMark Murray 275511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 276af12a3e7SDag-Erling Smørgrav hints.ai_family = family; 277511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 278511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 2791f5ce8f4SBrian Feldman if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 2801f5ce8f4SBrian Feldman fatal("%s: %.100s: %s", __progname, host, 281511b41d2SMark Murray gai_strerror(gaierr)); 282511b41d2SMark Murray 283511b41d2SMark Murray /* 284511b41d2SMark Murray * Try to connect several times. On some machines, the first time 285511b41d2SMark Murray * will sometimes fail. In general socket code appears to behave 286511b41d2SMark Murray * quite magically on many machines. 287511b41d2SMark Murray */ 288af12a3e7SDag-Erling Smørgrav for (attempt = 0; ;) { 289511b41d2SMark Murray if (attempt > 0) 290511b41d2SMark Murray debug("Trying again..."); 291511b41d2SMark Murray 292511b41d2SMark Murray /* Loop through addresses for this host, and try each one in 293511b41d2SMark Murray sequence until the connection succeeds. */ 294511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 295511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 296511b41d2SMark Murray continue; 297511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 298511b41d2SMark Murray ntop, sizeof(ntop), strport, sizeof(strport), 299511b41d2SMark Murray NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 300511b41d2SMark Murray error("ssh_connect: getnameinfo failed"); 301511b41d2SMark Murray continue; 302511b41d2SMark Murray } 303511b41d2SMark Murray debug("Connecting to %.200s [%.100s] port %s.", 3041f5ce8f4SBrian Feldman host, ntop, strport); 305511b41d2SMark Murray 306511b41d2SMark Murray /* Create a socket for connecting. */ 307ca3176e7SBrian Feldman sock = ssh_create_socket(pw, 308ca3176e7SBrian Feldman !anonymous && geteuid() == 0, 309511b41d2SMark Murray ai->ai_family); 310511b41d2SMark Murray if (sock < 0) 311af12a3e7SDag-Erling Smørgrav /* Any error is already output */ 312511b41d2SMark Murray continue; 313511b41d2SMark Murray 314511b41d2SMark Murray /* Connect to the host. We use the user's uid in the 315511b41d2SMark Murray * hope that it will help with tcp_wrappers showing 316511b41d2SMark Murray * the remote uid as root. 317511b41d2SMark Murray */ 318ca3176e7SBrian Feldman temporarily_use_uid(pw); 319511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { 320511b41d2SMark Murray /* Successful connection. */ 321c322fe35SKris Kennaway memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 322511b41d2SMark Murray restore_uid(); 323511b41d2SMark Murray break; 324511b41d2SMark Murray } else { 325af12a3e7SDag-Erling Smørgrav if (errno == ECONNREFUSED) 326af12a3e7SDag-Erling Smørgrav full_failure = 0; 327af12a3e7SDag-Erling Smørgrav log("ssh: connect to address %s port %s: %s", 328af12a3e7SDag-Erling Smørgrav sockaddr_ntop(ai->ai_addr), strport, 329af12a3e7SDag-Erling Smørgrav strerror(errno)); 330511b41d2SMark Murray restore_uid(); 331511b41d2SMark Murray /* 332511b41d2SMark Murray * Close the failed socket; there appear to 333511b41d2SMark Murray * be some problems when reusing a socket for 334511b41d2SMark Murray * which connect() has already returned an 335511b41d2SMark Murray * error. 336511b41d2SMark Murray */ 337511b41d2SMark Murray close(sock); 338511b41d2SMark Murray } 339511b41d2SMark Murray } 3401f5ce8f4SBrian Feldman if (ai) 341511b41d2SMark Murray break; /* Successful connection. */ 342511b41d2SMark Murray 343af12a3e7SDag-Erling Smørgrav attempt++; 344af12a3e7SDag-Erling Smørgrav if (attempt >= connection_attempts) 345af12a3e7SDag-Erling Smørgrav break; 346511b41d2SMark Murray /* Sleep a moment before retrying. */ 347511b41d2SMark Murray sleep(1); 348511b41d2SMark Murray } 349511b41d2SMark Murray 350511b41d2SMark Murray freeaddrinfo(aitop); 351511b41d2SMark Murray 352511b41d2SMark Murray /* Return failure if we didn't get a successful connection. */ 353511b41d2SMark Murray if (attempt >= connection_attempts) 354af12a3e7SDag-Erling Smørgrav return full_failure ? ECONNABORTED : ECONNREFUSED; 355511b41d2SMark Murray 356511b41d2SMark Murray debug("Connection established."); 357511b41d2SMark Murray 358511b41d2SMark Murray /* 359511b41d2SMark Murray * Set socket options. We would like the socket to disappear as soon 360511b41d2SMark Murray * as it has been closed for whatever reason. 361511b41d2SMark Murray */ 362511b41d2SMark Murray /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ 363511b41d2SMark Murray linger.l_onoff = 1; 364511b41d2SMark Murray linger.l_linger = 5; 365511b41d2SMark Murray setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); 366511b41d2SMark Murray 367ca3176e7SBrian Feldman /* Set keepalives if requested. */ 368ca3176e7SBrian Feldman if (options.keepalives && 369ca3176e7SBrian Feldman setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 370ca3176e7SBrian Feldman sizeof(on)) < 0) 371ca3176e7SBrian Feldman error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 372ca3176e7SBrian Feldman 373511b41d2SMark Murray /* Set the connection. */ 374511b41d2SMark Murray packet_set_connection(sock, sock); 375511b41d2SMark Murray 376af12a3e7SDag-Erling Smørgrav return 0; 377511b41d2SMark Murray } 378511b41d2SMark Murray 379511b41d2SMark Murray /* 380e8aafc91SKris Kennaway * Waits for the server identification string, and sends our own 381e8aafc91SKris Kennaway * identification string. 382511b41d2SMark Murray */ 383af12a3e7SDag-Erling Smørgrav static void 384ca3176e7SBrian Feldman ssh_exchange_identification(void) 385511b41d2SMark Murray { 386e8aafc91SKris Kennaway char buf[256], remote_version[256]; /* must be same size! */ 387e8aafc91SKris Kennaway int remote_major, remote_minor, i, mismatch; 388e8aafc91SKris Kennaway int connection_in = packet_get_connection_in(); 389e8aafc91SKris Kennaway int connection_out = packet_get_connection_out(); 390ca3176e7SBrian Feldman int minor1 = PROTOCOL_MINOR_1; 391511b41d2SMark Murray 392e8aafc91SKris Kennaway /* Read other side\'s version identification. */ 393c2d3a559SKris Kennaway for (;;) { 394e8aafc91SKris Kennaway for (i = 0; i < sizeof(buf) - 1; i++) { 395c2d3a559SKris Kennaway int len = atomicio(read, connection_in, &buf[i], 1); 396e8aafc91SKris Kennaway if (len < 0) 397e8aafc91SKris Kennaway fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); 398e8aafc91SKris Kennaway if (len != 1) 399e8aafc91SKris Kennaway fatal("ssh_exchange_identification: Connection closed by remote host"); 400e8aafc91SKris Kennaway if (buf[i] == '\r') { 401e8aafc91SKris Kennaway buf[i] = '\n'; 402e8aafc91SKris Kennaway buf[i + 1] = 0; 403e8aafc91SKris Kennaway continue; /**XXX wait for \n */ 404511b41d2SMark Murray } 405e8aafc91SKris Kennaway if (buf[i] == '\n') { 406e8aafc91SKris Kennaway buf[i + 1] = 0; 407511b41d2SMark Murray break; 408e8aafc91SKris Kennaway } 409e8aafc91SKris Kennaway } 410e8aafc91SKris Kennaway buf[sizeof(buf) - 1] = 0; 411c2d3a559SKris Kennaway if (strncmp(buf, "SSH-", 4) == 0) 412c2d3a559SKris Kennaway break; 413c2d3a559SKris Kennaway debug("ssh_exchange_identification: %s", buf); 414c2d3a559SKris Kennaway } 415e8aafc91SKris Kennaway server_version_string = xstrdup(buf); 416511b41d2SMark Murray 417511b41d2SMark Murray /* 418e8aafc91SKris Kennaway * Check that the versions match. In future this might accept 419e8aafc91SKris Kennaway * several versions and set appropriate flags to handle them. 420511b41d2SMark Murray */ 421e8aafc91SKris Kennaway if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 422e8aafc91SKris Kennaway &remote_major, &remote_minor, remote_version) != 3) 423e8aafc91SKris Kennaway fatal("Bad remote protocol version identification: '%.100s'", buf); 424e8aafc91SKris Kennaway debug("Remote protocol version %d.%d, remote software version %.100s", 425e8aafc91SKris Kennaway remote_major, remote_minor, remote_version); 426511b41d2SMark Murray 427e8aafc91SKris Kennaway compat_datafellows(remote_version); 428e8aafc91SKris Kennaway mismatch = 0; 429e8aafc91SKris Kennaway 430e8aafc91SKris Kennaway switch (remote_major) { 431e8aafc91SKris Kennaway case 1: 432e8aafc91SKris Kennaway if (remote_minor == 99 && 433e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) && 434e8aafc91SKris Kennaway !(options.protocol & SSH_PROTO_1_PREFERRED)) { 435e8aafc91SKris Kennaway enable_compat20(); 436511b41d2SMark Murray break; 437e8aafc91SKris Kennaway } 438e8aafc91SKris Kennaway if (!(options.protocol & SSH_PROTO_1)) { 439e8aafc91SKris Kennaway mismatch = 1; 440e8aafc91SKris Kennaway break; 441e8aafc91SKris Kennaway } 442e8aafc91SKris Kennaway if (remote_minor < 3) { 443e8aafc91SKris Kennaway fatal("Remote machine has too old SSH software version."); 444ca3176e7SBrian Feldman } else if (remote_minor == 3 || remote_minor == 4) { 445e8aafc91SKris Kennaway /* We speak 1.3, too. */ 446e8aafc91SKris Kennaway enable_compat13(); 447ca3176e7SBrian Feldman minor1 = 3; 448e8aafc91SKris Kennaway if (options.forward_agent) { 449e8aafc91SKris Kennaway log("Agent forwarding disabled for protocol 1.3"); 450e8aafc91SKris Kennaway options.forward_agent = 0; 451e8aafc91SKris Kennaway } 452e8aafc91SKris Kennaway } 453e8aafc91SKris Kennaway break; 454e8aafc91SKris Kennaway case 2: 455e8aafc91SKris Kennaway if (options.protocol & SSH_PROTO_2) { 456e8aafc91SKris Kennaway enable_compat20(); 457e8aafc91SKris Kennaway break; 458e8aafc91SKris Kennaway } 459e8aafc91SKris Kennaway /* FALLTHROUGH */ 460511b41d2SMark Murray default: 461e8aafc91SKris Kennaway mismatch = 1; 462e8aafc91SKris Kennaway break; 463511b41d2SMark Murray } 464e8aafc91SKris Kennaway if (mismatch) 465e8aafc91SKris Kennaway fatal("Protocol major versions differ: %d vs. %d", 466e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 467e8aafc91SKris Kennaway remote_major); 468e8aafc91SKris Kennaway /* Send our own protocol version identification. */ 469e8aafc91SKris Kennaway snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 470e8aafc91SKris Kennaway compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 471ca3176e7SBrian Feldman compat20 ? PROTOCOL_MINOR_2 : minor1, 472e8aafc91SKris Kennaway SSH_VERSION); 473e8aafc91SKris Kennaway if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) 474e8aafc91SKris Kennaway fatal("write: %.100s", strerror(errno)); 475e8aafc91SKris Kennaway client_version_string = xstrdup(buf); 476e8aafc91SKris Kennaway chop(client_version_string); 477e8aafc91SKris Kennaway chop(server_version_string); 478e8aafc91SKris Kennaway debug("Local version string %.100s", client_version_string); 479511b41d2SMark Murray } 480511b41d2SMark Murray 481ca3176e7SBrian Feldman /* defaults to 'no' */ 482af12a3e7SDag-Erling Smørgrav static int 483af12a3e7SDag-Erling Smørgrav confirm(const char *prompt) 484511b41d2SMark Murray { 485af12a3e7SDag-Erling Smørgrav const char *msg, *again = "Please type 'yes' or 'no': "; 486af12a3e7SDag-Erling Smørgrav char *p; 487af12a3e7SDag-Erling Smørgrav int ret = -1; 488511b41d2SMark Murray 489ca3176e7SBrian Feldman if (options.batch_mode) 490ca3176e7SBrian Feldman return 0; 491af12a3e7SDag-Erling Smørgrav for (msg = prompt;;msg = again) { 492af12a3e7SDag-Erling Smørgrav p = read_passphrase(msg, RP_ECHO); 493af12a3e7SDag-Erling Smørgrav if (p == NULL || 494af12a3e7SDag-Erling Smørgrav (p[0] == '\0') || (p[0] == '\n') || 495af12a3e7SDag-Erling Smørgrav strncasecmp(p, "no", 2) == 0) 496af12a3e7SDag-Erling Smørgrav ret = 0; 497af12a3e7SDag-Erling Smørgrav if (strncasecmp(p, "yes", 3) == 0) 498af12a3e7SDag-Erling Smørgrav ret = 1; 499af12a3e7SDag-Erling Smørgrav if (p) 500af12a3e7SDag-Erling Smørgrav xfree(p); 501af12a3e7SDag-Erling Smørgrav if (ret != -1) 502af12a3e7SDag-Erling Smørgrav return ret; 503511b41d2SMark Murray } 504511b41d2SMark Murray } 505511b41d2SMark Murray 506e8aafc91SKris Kennaway /* 507af12a3e7SDag-Erling Smørgrav * check whether the supplied host key is valid, return -1 if the key 508af12a3e7SDag-Erling Smørgrav * is not valid. the user_hostfile will not be updated if 'readonly' is true. 509e8aafc91SKris Kennaway */ 510e8aafc91SKris Kennaway 511af12a3e7SDag-Erling Smørgrav static int 512e8aafc91SKris Kennaway check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, 513af12a3e7SDag-Erling Smørgrav int readonly, const char *user_hostfile, const char *system_hostfile) 514511b41d2SMark Murray { 515e8aafc91SKris Kennaway Key *file_key; 516e8aafc91SKris Kennaway char *type = key_type(host_key); 517e8aafc91SKris Kennaway char *ip = NULL; 518ca3176e7SBrian Feldman char hostline[1000], *hostp, *fp; 519e8aafc91SKris Kennaway HostStatus host_status; 520e8aafc91SKris Kennaway HostStatus ip_status; 521e8aafc91SKris Kennaway int local = 0, host_ip_differ = 0; 522e8aafc91SKris Kennaway char ntop[NI_MAXHOST]; 523af12a3e7SDag-Erling Smørgrav char msg[1024]; 524af12a3e7SDag-Erling Smørgrav int len, host_line, ip_line; 525ca3176e7SBrian Feldman const char *host_file = NULL, *ip_file = NULL; 526511b41d2SMark Murray 527e8aafc91SKris Kennaway /* 528e8aafc91SKris Kennaway * Force accepting of the host key for loopback/localhost. The 529e8aafc91SKris Kennaway * problem is that if the home directory is NFS-mounted to multiple 530e8aafc91SKris Kennaway * machines, localhost will refer to a different machine in each of 531e8aafc91SKris Kennaway * them, and the user will get bogus HOST_CHANGED warnings. This 532e8aafc91SKris Kennaway * essentially disables host authentication for localhost; however, 533e8aafc91SKris Kennaway * this is probably not a real problem. 534e8aafc91SKris Kennaway */ 535e8aafc91SKris Kennaway /** hostaddr == 0! */ 536e8aafc91SKris Kennaway switch (hostaddr->sa_family) { 537e8aafc91SKris Kennaway case AF_INET: 538af12a3e7SDag-Erling Smørgrav local = (ntohl(((struct sockaddr_in *)hostaddr)-> 539af12a3e7SDag-Erling Smørgrav sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 540511b41d2SMark Murray break; 541e8aafc91SKris Kennaway case AF_INET6: 542af12a3e7SDag-Erling Smørgrav local = IN6_IS_ADDR_LOOPBACK( 543af12a3e7SDag-Erling Smørgrav &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 544511b41d2SMark Murray break; 545e8aafc91SKris Kennaway default: 546e8aafc91SKris Kennaway local = 0; 547511b41d2SMark Murray break; 548511b41d2SMark Murray } 549af12a3e7SDag-Erling Smørgrav if (options.no_host_authentication_for_localhost == 1 && local && 550af12a3e7SDag-Erling Smørgrav options.host_key_alias == NULL) { 551ca3176e7SBrian Feldman debug("Forcing accepting of host key for " 552ca3176e7SBrian Feldman "loopback/localhost."); 553af12a3e7SDag-Erling Smørgrav return 0; 554511b41d2SMark Murray } 555511b41d2SMark Murray 556e8aafc91SKris Kennaway /* 557ca3176e7SBrian Feldman * We don't have the remote ip-address for connections 558ca3176e7SBrian Feldman * using a proxy command 559e8aafc91SKris Kennaway */ 560ca3176e7SBrian Feldman if (options.proxy_command == NULL) { 561e8aafc91SKris Kennaway if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop), 562e8aafc91SKris Kennaway NULL, 0, NI_NUMERICHOST) != 0) 563e8aafc91SKris Kennaway fatal("check_host_key: getnameinfo failed"); 564e8aafc91SKris Kennaway ip = xstrdup(ntop); 565ca3176e7SBrian Feldman } else { 566ca3176e7SBrian Feldman ip = xstrdup("<no hostip for proxy command>"); 567ca3176e7SBrian Feldman } 568ca3176e7SBrian Feldman /* 569ca3176e7SBrian Feldman * Turn off check_host_ip if the connection is to localhost, via proxy 570ca3176e7SBrian Feldman * command or if we don't have a hostname to compare with 571ca3176e7SBrian Feldman */ 572ca3176e7SBrian Feldman if (options.check_host_ip && 573ca3176e7SBrian Feldman (local || strcmp(host, ip) == 0 || options.proxy_command != NULL)) 574ca3176e7SBrian Feldman options.check_host_ip = 0; 575ca3176e7SBrian Feldman 576ca3176e7SBrian Feldman /* 577ca3176e7SBrian Feldman * Allow the user to record the key under a different name. This is 578ca3176e7SBrian Feldman * useful for ssh tunneling over forwarded connections or if you run 579ca3176e7SBrian Feldman * multiple sshd's on different ports on the same machine. 580ca3176e7SBrian Feldman */ 581ca3176e7SBrian Feldman if (options.host_key_alias != NULL) { 582ca3176e7SBrian Feldman host = options.host_key_alias; 583ca3176e7SBrian Feldman debug("using hostkeyalias: %s", host); 584e8aafc91SKris Kennaway } 585e8aafc91SKris Kennaway 586e8aafc91SKris Kennaway /* 587e8aafc91SKris Kennaway * Store the host key from the known host file in here so that we can 588e8aafc91SKris Kennaway * compare it with the key for the IP address. 589e8aafc91SKris Kennaway */ 590e8aafc91SKris Kennaway file_key = key_new(host_key->type); 591e8aafc91SKris Kennaway 592e8aafc91SKris Kennaway /* 593e8aafc91SKris Kennaway * Check if the host key is present in the user\'s list of known 594e8aafc91SKris Kennaway * hosts or in the systemwide list. 595e8aafc91SKris Kennaway */ 596ca3176e7SBrian Feldman host_file = user_hostfile; 597af12a3e7SDag-Erling Smørgrav host_status = check_host_in_hostfile(host_file, host, host_key, 598af12a3e7SDag-Erling Smørgrav file_key, &host_line); 599ca3176e7SBrian Feldman if (host_status == HOST_NEW) { 600ca3176e7SBrian Feldman host_file = system_hostfile; 601af12a3e7SDag-Erling Smørgrav host_status = check_host_in_hostfile(host_file, host, host_key, 602af12a3e7SDag-Erling Smørgrav file_key, &host_line); 603ca3176e7SBrian Feldman } 604e8aafc91SKris Kennaway /* 605e8aafc91SKris Kennaway * Also perform check for the ip address, skip the check if we are 606e8aafc91SKris Kennaway * localhost or the hostname was an ip address to begin with 607e8aafc91SKris Kennaway */ 608ca3176e7SBrian Feldman if (options.check_host_ip) { 609e8aafc91SKris Kennaway Key *ip_key = key_new(host_key->type); 610e8aafc91SKris Kennaway 611ca3176e7SBrian Feldman ip_file = user_hostfile; 612af12a3e7SDag-Erling Smørgrav ip_status = check_host_in_hostfile(ip_file, ip, host_key, 613af12a3e7SDag-Erling Smørgrav ip_key, &ip_line); 614ca3176e7SBrian Feldman if (ip_status == HOST_NEW) { 615ca3176e7SBrian Feldman ip_file = system_hostfile; 616af12a3e7SDag-Erling Smørgrav ip_status = check_host_in_hostfile(ip_file, ip, 617af12a3e7SDag-Erling Smørgrav host_key, ip_key, &ip_line); 618ca3176e7SBrian Feldman } 619e8aafc91SKris Kennaway if (host_status == HOST_CHANGED && 620e8aafc91SKris Kennaway (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) 621e8aafc91SKris Kennaway host_ip_differ = 1; 622e8aafc91SKris Kennaway 623e8aafc91SKris Kennaway key_free(ip_key); 624e8aafc91SKris Kennaway } else 625e8aafc91SKris Kennaway ip_status = host_status; 626e8aafc91SKris Kennaway 627e8aafc91SKris Kennaway key_free(file_key); 628e8aafc91SKris Kennaway 629e8aafc91SKris Kennaway switch (host_status) { 630e8aafc91SKris Kennaway case HOST_OK: 631e8aafc91SKris Kennaway /* The host is known and the key matches. */ 632e8aafc91SKris Kennaway debug("Host '%.200s' is known and matches the %s host key.", 633e8aafc91SKris Kennaway host, type); 634ca3176e7SBrian Feldman debug("Found key in %s:%d", host_file, host_line); 635ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 636af12a3e7SDag-Erling Smørgrav if (readonly) 637af12a3e7SDag-Erling Smørgrav log("%s host key for IP address " 638af12a3e7SDag-Erling Smørgrav "'%.128s' not in list of known hosts.", 639e8aafc91SKris Kennaway type, ip); 640af12a3e7SDag-Erling Smørgrav else if (!add_host_to_hostfile(user_hostfile, ip, 641af12a3e7SDag-Erling Smørgrav host_key)) 642af12a3e7SDag-Erling Smørgrav log("Failed to add the %s host key for IP " 643af12a3e7SDag-Erling Smørgrav "address '%.128s' to the list of known " 644af12a3e7SDag-Erling Smørgrav "hosts (%.30s).", type, ip, user_hostfile); 645af12a3e7SDag-Erling Smørgrav else 646af12a3e7SDag-Erling Smørgrav log("Warning: Permanently added the %s host " 647af12a3e7SDag-Erling Smørgrav "key for IP address '%.128s' to the list " 648af12a3e7SDag-Erling Smørgrav "of known hosts.", type, ip); 649e8aafc91SKris Kennaway } 650e8aafc91SKris Kennaway break; 651e8aafc91SKris Kennaway case HOST_NEW: 652af12a3e7SDag-Erling Smørgrav if (readonly) 653af12a3e7SDag-Erling Smørgrav goto fail; 654e8aafc91SKris Kennaway /* The host is new. */ 655e8aafc91SKris Kennaway if (options.strict_host_key_checking == 1) { 656af12a3e7SDag-Erling Smørgrav /* 657af12a3e7SDag-Erling Smørgrav * User has requested strict host key checking. We 658af12a3e7SDag-Erling Smørgrav * will not add the host key automatically. The only 659af12a3e7SDag-Erling Smørgrav * alternative left is to abort. 660af12a3e7SDag-Erling Smørgrav */ 661af12a3e7SDag-Erling Smørgrav error("No %s host key is known for %.200s and you " 662af12a3e7SDag-Erling Smørgrav "have requested strict checking.", type, host); 663af12a3e7SDag-Erling Smørgrav goto fail; 664e8aafc91SKris Kennaway } else if (options.strict_host_key_checking == 2) { 665e8aafc91SKris Kennaway /* The default */ 666ca3176e7SBrian Feldman fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 667af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 668af12a3e7SDag-Erling Smørgrav "The authenticity of host '%.200s (%s)' can't be " 669af12a3e7SDag-Erling Smørgrav "established.\n" 670e8aafc91SKris Kennaway "%s key fingerprint is %s.\n" 671af12a3e7SDag-Erling Smørgrav "Are you sure you want to continue connecting " 672af12a3e7SDag-Erling Smørgrav "(yes/no)? ", host, ip, type, fp); 673ca3176e7SBrian Feldman xfree(fp); 674af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 675af12a3e7SDag-Erling Smørgrav goto fail; 676e8aafc91SKris Kennaway } 677ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 678e8aafc91SKris Kennaway snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 679e8aafc91SKris Kennaway hostp = hostline; 680e8aafc91SKris Kennaway } else 681e8aafc91SKris Kennaway hostp = host; 682e8aafc91SKris Kennaway 683af12a3e7SDag-Erling Smørgrav /* 684af12a3e7SDag-Erling Smørgrav * If not in strict mode, add the key automatically to the 685af12a3e7SDag-Erling Smørgrav * local known_hosts file. 686af12a3e7SDag-Erling Smørgrav */ 687e8aafc91SKris Kennaway if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) 688af12a3e7SDag-Erling Smørgrav log("Failed to add the host to the list of known " 689af12a3e7SDag-Erling Smørgrav "hosts (%.500s).", user_hostfile); 690e8aafc91SKris Kennaway else 691af12a3e7SDag-Erling Smørgrav log("Warning: Permanently added '%.200s' (%s) to the " 692af12a3e7SDag-Erling Smørgrav "list of known hosts.", hostp, type); 693e8aafc91SKris Kennaway break; 694e8aafc91SKris Kennaway case HOST_CHANGED: 695e8aafc91SKris Kennaway if (options.check_host_ip && host_ip_differ) { 696e8aafc91SKris Kennaway char *msg; 697e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 698e8aafc91SKris Kennaway msg = "is unknown"; 699e8aafc91SKris Kennaway else if (ip_status == HOST_OK) 700e8aafc91SKris Kennaway msg = "is unchanged"; 701e8aafc91SKris Kennaway else 702e8aafc91SKris Kennaway msg = "has a different value"; 703e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 704e8aafc91SKris Kennaway error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 705e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 706e8aafc91SKris Kennaway error("The %s host key for %s has changed,", type, host); 707e8aafc91SKris Kennaway error("and the key for the according IP address %s", ip); 708e8aafc91SKris Kennaway error("%s. This could either mean that", msg); 709e8aafc91SKris Kennaway error("DNS SPOOFING is happening or the IP address for the host"); 710ca3176e7SBrian Feldman error("and its host key have changed at the same time."); 711ca3176e7SBrian Feldman if (ip_status != HOST_NEW) 712ca3176e7SBrian Feldman error("Offending key for IP in %s:%d", ip_file, ip_line); 713e8aafc91SKris Kennaway } 714e8aafc91SKris Kennaway /* The host key has changed. */ 715ca3176e7SBrian Feldman fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 716e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 717e8aafc91SKris Kennaway error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 718e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 719e8aafc91SKris Kennaway error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 720e8aafc91SKris Kennaway error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 721e8aafc91SKris Kennaway error("It is also possible that the %s host key has just been changed.", type); 722ca3176e7SBrian Feldman error("The fingerprint for the %s key sent by the remote host is\n%s.", 723ca3176e7SBrian Feldman type, fp); 724e8aafc91SKris Kennaway error("Please contact your system administrator."); 725e8aafc91SKris Kennaway error("Add correct host key in %.100s to get rid of this message.", 726e8aafc91SKris Kennaway user_hostfile); 727ca3176e7SBrian Feldman error("Offending key in %s:%d", host_file, host_line); 728ca3176e7SBrian Feldman xfree(fp); 729e8aafc91SKris Kennaway 730e8aafc91SKris Kennaway /* 731e8aafc91SKris Kennaway * If strict host key checking is in use, the user will have 732e8aafc91SKris Kennaway * to edit the key manually and we can only abort. 733e8aafc91SKris Kennaway */ 734af12a3e7SDag-Erling Smørgrav if (options.strict_host_key_checking) { 735af12a3e7SDag-Erling Smørgrav error("%s host key for %.200s has changed and you have " 736af12a3e7SDag-Erling Smørgrav "requested strict checking.", type, host); 737af12a3e7SDag-Erling Smørgrav goto fail; 738af12a3e7SDag-Erling Smørgrav } 739e8aafc91SKris Kennaway 740e8aafc91SKris Kennaway /* 741e8aafc91SKris Kennaway * If strict host key checking has not been requested, allow 742e8aafc91SKris Kennaway * the connection but without password authentication or 743e8aafc91SKris Kennaway * agent forwarding. 744e8aafc91SKris Kennaway */ 745e8aafc91SKris Kennaway if (options.password_authentication) { 746af12a3e7SDag-Erling Smørgrav error("Password authentication is disabled to avoid " 747af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 748e8aafc91SKris Kennaway options.password_authentication = 0; 749e8aafc91SKris Kennaway } 750e8aafc91SKris Kennaway if (options.forward_agent) { 751af12a3e7SDag-Erling Smørgrav error("Agent forwarding is disabled to avoid " 752af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 753e8aafc91SKris Kennaway options.forward_agent = 0; 754e8aafc91SKris Kennaway } 755ca3176e7SBrian Feldman if (options.forward_x11) { 756af12a3e7SDag-Erling Smørgrav error("X11 forwarding is disabled to avoid " 757af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 758ca3176e7SBrian Feldman options.forward_x11 = 0; 759ca3176e7SBrian Feldman } 760af12a3e7SDag-Erling Smørgrav if (options.num_local_forwards > 0 || 761af12a3e7SDag-Erling Smørgrav options.num_remote_forwards > 0) { 762af12a3e7SDag-Erling Smørgrav error("Port forwarding is disabled to avoid " 763af12a3e7SDag-Erling Smørgrav "man-in-the-middle attacks."); 764af12a3e7SDag-Erling Smørgrav options.num_local_forwards = 765af12a3e7SDag-Erling Smørgrav options.num_remote_forwards = 0; 766ca3176e7SBrian Feldman } 767e8aafc91SKris Kennaway /* 768e8aafc91SKris Kennaway * XXX Should permit the user to change to use the new id. 769e8aafc91SKris Kennaway * This could be done by converting the host key to an 770e8aafc91SKris Kennaway * identifying sentence, tell that the host identifies itself 771e8aafc91SKris Kennaway * by that sentence, and ask the user if he/she whishes to 772e8aafc91SKris Kennaway * accept the authentication. 773e8aafc91SKris Kennaway */ 774e8aafc91SKris Kennaway break; 775e8aafc91SKris Kennaway } 776ca3176e7SBrian Feldman 777ca3176e7SBrian Feldman if (options.check_host_ip && host_status != HOST_CHANGED && 778ca3176e7SBrian Feldman ip_status == HOST_CHANGED) { 779af12a3e7SDag-Erling Smørgrav snprintf(msg, sizeof(msg), 780af12a3e7SDag-Erling Smørgrav "Warning: the %s host key for '%.200s' " 781af12a3e7SDag-Erling Smørgrav "differs from the key for the IP address '%.128s'" 782af12a3e7SDag-Erling Smørgrav "\nOffending key for IP in %s:%d", 783af12a3e7SDag-Erling Smørgrav type, host, ip, ip_file, ip_line); 784af12a3e7SDag-Erling Smørgrav if (host_status == HOST_OK) { 785af12a3e7SDag-Erling Smørgrav len = strlen(msg); 786af12a3e7SDag-Erling Smørgrav snprintf(msg + len, sizeof(msg) - len, 787af12a3e7SDag-Erling Smørgrav "\nMatching host key in %s:%d", 788af12a3e7SDag-Erling Smørgrav host_file, host_line); 789af12a3e7SDag-Erling Smørgrav } 790ca3176e7SBrian Feldman if (options.strict_host_key_checking == 1) { 791af12a3e7SDag-Erling Smørgrav log(msg); 792af12a3e7SDag-Erling Smørgrav error("Exiting, you have requested strict checking."); 793af12a3e7SDag-Erling Smørgrav goto fail; 794ca3176e7SBrian Feldman } else if (options.strict_host_key_checking == 2) { 795af12a3e7SDag-Erling Smørgrav strlcat(msg, "\nAre you sure you want " 796af12a3e7SDag-Erling Smørgrav "to continue connecting (yes/no)? ", sizeof(msg)); 797af12a3e7SDag-Erling Smørgrav if (!confirm(msg)) 798af12a3e7SDag-Erling Smørgrav goto fail; 799af12a3e7SDag-Erling Smørgrav } else { 800af12a3e7SDag-Erling Smørgrav log(msg); 801ca3176e7SBrian Feldman } 802ca3176e7SBrian Feldman } 803ca3176e7SBrian Feldman 804e8aafc91SKris Kennaway xfree(ip); 805af12a3e7SDag-Erling Smørgrav return 0; 806af12a3e7SDag-Erling Smørgrav 807af12a3e7SDag-Erling Smørgrav fail: 808af12a3e7SDag-Erling Smørgrav xfree(ip); 809af12a3e7SDag-Erling Smørgrav return -1; 810e8aafc91SKris Kennaway } 811511b41d2SMark Murray 812fe5fd017SMark Murray int 813af12a3e7SDag-Erling Smørgrav verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) 814fe5fd017SMark Murray { 815af12a3e7SDag-Erling Smørgrav struct stat st; 816fe5fd017SMark Murray 817af12a3e7SDag-Erling Smørgrav /* return ok if the key can be found in an old keyfile */ 818af12a3e7SDag-Erling Smørgrav if (stat(options.system_hostfile2, &st) == 0 || 819af12a3e7SDag-Erling Smørgrav stat(options.user_hostfile2, &st) == 0) { 820af12a3e7SDag-Erling Smørgrav if (check_host_key(host, hostaddr, host_key, /*readonly*/ 1, 821af12a3e7SDag-Erling Smørgrav options.user_hostfile2, options.system_hostfile2) == 0) 822af12a3e7SDag-Erling Smørgrav return 0; 823fe5fd017SMark Murray } 824af12a3e7SDag-Erling Smørgrav return check_host_key(host, hostaddr, host_key, /*readonly*/ 0, 825af12a3e7SDag-Erling Smørgrav options.user_hostfile, options.system_hostfile); 826fe5fd017SMark Murray } 827fe5fd017SMark Murray 828511b41d2SMark Murray /* 829511b41d2SMark Murray * Starts a dialog with the server, and authenticates the current user on the 830511b41d2SMark Murray * server. This does not need any extra privileges. The basic connection 831511b41d2SMark Murray * to the server must already have been established before this is called. 832511b41d2SMark Murray * If login fails, this function prints an error and never returns. 833511b41d2SMark Murray * This function does not require super-user privileges. 834511b41d2SMark Murray */ 835511b41d2SMark Murray void 836ca3176e7SBrian Feldman ssh_login(Key **keys, int nkeys, const char *orighost, 837ca3176e7SBrian Feldman struct sockaddr *hostaddr, struct passwd *pw) 838511b41d2SMark Murray { 839511b41d2SMark Murray char *host, *cp; 840e8aafc91SKris Kennaway char *server_user, *local_user; 841e8aafc91SKris Kennaway 842e8aafc91SKris Kennaway local_user = xstrdup(pw->pw_name); 843e8aafc91SKris Kennaway server_user = options.user ? options.user : local_user; 844511b41d2SMark Murray 845511b41d2SMark Murray /* Convert the user-supplied hostname into all lowercase. */ 846511b41d2SMark Murray host = xstrdup(orighost); 847511b41d2SMark Murray for (cp = host; *cp; cp++) 848511b41d2SMark Murray if (isupper(*cp)) 849511b41d2SMark Murray *cp = tolower(*cp); 850511b41d2SMark Murray 851511b41d2SMark Murray /* Exchange protocol version identification strings with the server. */ 852511b41d2SMark Murray ssh_exchange_identification(); 853511b41d2SMark Murray 854511b41d2SMark Murray /* Put the connection into non-blocking mode. */ 855511b41d2SMark Murray packet_set_nonblocking(); 856511b41d2SMark Murray 857511b41d2SMark Murray /* key exchange */ 858511b41d2SMark Murray /* authenticate user */ 859e8aafc91SKris Kennaway if (compat20) { 860e8aafc91SKris Kennaway ssh_kex2(host, hostaddr); 861ca3176e7SBrian Feldman ssh_userauth2(local_user, server_user, host, keys, nkeys); 862e8aafc91SKris Kennaway } else { 863e8aafc91SKris Kennaway ssh_kex(host, hostaddr); 864ca3176e7SBrian Feldman ssh_userauth1(local_user, server_user, host, keys, nkeys); 865e8aafc91SKris Kennaway } 866511b41d2SMark Murray } 867e0fbb1d2SBrian Feldman 868e0fbb1d2SBrian Feldman void 869e0fbb1d2SBrian Feldman ssh_put_password(char *password) 870e0fbb1d2SBrian Feldman { 871e0fbb1d2SBrian Feldman int size; 872e0fbb1d2SBrian Feldman char *padded; 873e0fbb1d2SBrian Feldman 874ca3176e7SBrian Feldman if (datafellows & SSH_BUG_PASSWORDPAD) { 875af12a3e7SDag-Erling Smørgrav packet_put_cstring(password); 876ca3176e7SBrian Feldman return; 877ca3176e7SBrian Feldman } 878e0fbb1d2SBrian Feldman size = roundup(strlen(password) + 1, 32); 879e0fbb1d2SBrian Feldman padded = xmalloc(size); 880e0fbb1d2SBrian Feldman memset(padded, 0, size); 881e0fbb1d2SBrian Feldman strlcpy(padded, password, size); 882e0fbb1d2SBrian Feldman packet_put_string(padded, size); 883e0fbb1d2SBrian Feldman memset(padded, 0, size); 884e0fbb1d2SBrian Feldman xfree(padded); 885e0fbb1d2SBrian Feldman } 886