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" 16ca3176e7SBrian Feldman RCSID("$OpenBSD: sshconnect.c,v 1.104 2001/04/12 19:15:25 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" 35511b41d2SMark Murray 36e8aafc91SKris Kennaway char *client_version_string = NULL; 37e8aafc91SKris Kennaway char *server_version_string = NULL; 38511b41d2SMark Murray 39511b41d2SMark Murray extern Options options; 40511b41d2SMark Murray extern char *__progname; 41511b41d2SMark Murray 42ca3176e7SBrian Feldman /* AF_UNSPEC or AF_INET or AF_INET6 */ 43ca3176e7SBrian Feldman extern int IPv4or6; 44ca3176e7SBrian Feldman 45511b41d2SMark Murray /* 46511b41d2SMark Murray * Connect to the given ssh server using a proxy command. 47511b41d2SMark Murray */ 48511b41d2SMark Murray int 49ca3176e7SBrian Feldman ssh_proxy_connect(const char *host, u_short port, struct passwd *pw, 50511b41d2SMark Murray const char *proxy_command) 51511b41d2SMark Murray { 52511b41d2SMark Murray Buffer command; 53511b41d2SMark Murray const char *cp; 54511b41d2SMark Murray char *command_string; 55511b41d2SMark Murray int pin[2], pout[2]; 56e8aafc91SKris Kennaway pid_t pid; 57511b41d2SMark Murray char strport[NI_MAXSERV]; 58511b41d2SMark Murray 59511b41d2SMark Murray /* Convert the port number into a string. */ 60511b41d2SMark Murray snprintf(strport, sizeof strport, "%hu", port); 61511b41d2SMark Murray 62511b41d2SMark Murray /* Build the final command string in the buffer by making the 63511b41d2SMark Murray appropriate substitutions to the given proxy command. */ 64511b41d2SMark Murray buffer_init(&command); 65511b41d2SMark Murray for (cp = proxy_command; *cp; cp++) { 66511b41d2SMark Murray if (cp[0] == '%' && cp[1] == '%') { 67511b41d2SMark Murray buffer_append(&command, "%", 1); 68511b41d2SMark Murray cp++; 69511b41d2SMark Murray continue; 70511b41d2SMark Murray } 71511b41d2SMark Murray if (cp[0] == '%' && cp[1] == 'h') { 72511b41d2SMark Murray buffer_append(&command, host, strlen(host)); 73511b41d2SMark Murray cp++; 74511b41d2SMark Murray continue; 75511b41d2SMark Murray } 76511b41d2SMark Murray if (cp[0] == '%' && cp[1] == 'p') { 77511b41d2SMark Murray buffer_append(&command, strport, strlen(strport)); 78511b41d2SMark Murray cp++; 79511b41d2SMark Murray continue; 80511b41d2SMark Murray } 81511b41d2SMark Murray buffer_append(&command, cp, 1); 82511b41d2SMark Murray } 83511b41d2SMark Murray buffer_append(&command, "\0", 1); 84511b41d2SMark Murray 85511b41d2SMark Murray /* Get the final command string. */ 86511b41d2SMark Murray command_string = buffer_ptr(&command); 87511b41d2SMark Murray 88511b41d2SMark Murray /* Create pipes for communicating with the proxy. */ 89511b41d2SMark Murray if (pipe(pin) < 0 || pipe(pout) < 0) 90511b41d2SMark Murray fatal("Could not create pipes to communicate with the proxy: %.100s", 91511b41d2SMark Murray strerror(errno)); 92511b41d2SMark Murray 93511b41d2SMark Murray debug("Executing proxy command: %.500s", command_string); 94511b41d2SMark Murray 95511b41d2SMark Murray /* Fork and execute the proxy command. */ 96511b41d2SMark Murray if ((pid = fork()) == 0) { 97511b41d2SMark Murray char *argv[10]; 98511b41d2SMark Murray 99511b41d2SMark Murray /* Child. Permanently give up superuser privileges. */ 100ca3176e7SBrian Feldman permanently_set_uid(pw); 101511b41d2SMark Murray 102511b41d2SMark Murray /* Redirect stdin and stdout. */ 103511b41d2SMark Murray close(pin[1]); 104511b41d2SMark Murray if (pin[0] != 0) { 105511b41d2SMark Murray if (dup2(pin[0], 0) < 0) 106511b41d2SMark Murray perror("dup2 stdin"); 107511b41d2SMark Murray close(pin[0]); 108511b41d2SMark Murray } 109511b41d2SMark Murray close(pout[0]); 110511b41d2SMark Murray if (dup2(pout[1], 1) < 0) 111511b41d2SMark Murray perror("dup2 stdout"); 112511b41d2SMark Murray /* Cannot be 1 because pin allocated two descriptors. */ 113511b41d2SMark Murray close(pout[1]); 114511b41d2SMark Murray 115511b41d2SMark Murray /* Stderr is left as it is so that error messages get 116511b41d2SMark Murray printed on the user's terminal. */ 117ca3176e7SBrian Feldman argv[0] = _PATH_BSHELL; 118511b41d2SMark Murray argv[1] = "-c"; 119511b41d2SMark Murray argv[2] = command_string; 120511b41d2SMark Murray argv[3] = NULL; 121511b41d2SMark Murray 122511b41d2SMark Murray /* Execute the proxy command. Note that we gave up any 123511b41d2SMark Murray extra privileges above. */ 124ca3176e7SBrian Feldman execv(argv[0], argv); 125ca3176e7SBrian Feldman perror(argv[0]); 126511b41d2SMark Murray exit(1); 127511b41d2SMark Murray } 128511b41d2SMark Murray /* Parent. */ 129511b41d2SMark Murray if (pid < 0) 130511b41d2SMark Murray fatal("fork failed: %.100s", strerror(errno)); 131511b41d2SMark Murray 132511b41d2SMark Murray /* Close child side of the descriptors. */ 133511b41d2SMark Murray close(pin[0]); 134511b41d2SMark Murray close(pout[1]); 135511b41d2SMark Murray 136511b41d2SMark Murray /* Free the command name. */ 137511b41d2SMark Murray buffer_free(&command); 138511b41d2SMark Murray 139511b41d2SMark Murray /* Set the connection file descriptors. */ 140511b41d2SMark Murray packet_set_connection(pout[0], pin[1]); 141511b41d2SMark Murray 142511b41d2SMark Murray return 1; 143511b41d2SMark Murray } 144511b41d2SMark Murray 145511b41d2SMark Murray /* 146511b41d2SMark Murray * Creates a (possibly privileged) socket for use as the ssh connection. 147511b41d2SMark Murray */ 148511b41d2SMark Murray int 149ca3176e7SBrian Feldman ssh_create_socket(struct passwd *pw, int privileged, int family) 150511b41d2SMark Murray { 151511b41d2SMark Murray int sock; 152511b41d2SMark Murray 153511b41d2SMark Murray /* 154511b41d2SMark Murray * If we are running as root and want to connect to a privileged 155511b41d2SMark Murray * port, bind our own socket to a privileged port. 156511b41d2SMark Murray */ 157511b41d2SMark Murray if (privileged) { 158511b41d2SMark Murray int p = IPPORT_RESERVED - 1; 159511b41d2SMark Murray sock = rresvport_af(&p, family); 160511b41d2SMark Murray if (sock < 0) 161511b41d2SMark Murray error("rresvport: af=%d %.100s", family, strerror(errno)); 162511b41d2SMark Murray else 163511b41d2SMark Murray debug("Allocated local port %d.", p); 164511b41d2SMark Murray } else { 165511b41d2SMark Murray /* 166511b41d2SMark Murray * Just create an ordinary socket on arbitrary port. We use 167511b41d2SMark Murray * the user's uid to create the socket. 168511b41d2SMark Murray */ 169ca3176e7SBrian Feldman temporarily_use_uid(pw); 170511b41d2SMark Murray sock = socket(family, SOCK_STREAM, 0); 171511b41d2SMark Murray if (sock < 0) 172511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 173511b41d2SMark Murray restore_uid(); 174511b41d2SMark Murray } 175511b41d2SMark Murray return sock; 176511b41d2SMark Murray } 177511b41d2SMark Murray 178511b41d2SMark Murray /* 179511b41d2SMark Murray * Opens a TCP/IP connection to the remote server on the given host. 180511b41d2SMark Murray * The address of the remote host will be returned in hostaddr. 181511b41d2SMark Murray * If port is 0, the default port will be used. If anonymous is zero, 182511b41d2SMark Murray * a privileged port will be allocated to make the connection. 183511b41d2SMark Murray * This requires super-user privileges if anonymous is false. 184511b41d2SMark Murray * Connection_attempts specifies the maximum number of tries (one per 185511b41d2SMark Murray * second). If proxy_command is non-NULL, it specifies the command (with %h 186511b41d2SMark Murray * and %p substituted for host and port, respectively) to use to contact 187511b41d2SMark Murray * the daemon. 188511b41d2SMark Murray */ 189511b41d2SMark Murray int 1901f5ce8f4SBrian Feldman ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 191511b41d2SMark Murray u_short port, int connection_attempts, 192ca3176e7SBrian Feldman int anonymous, struct passwd *pw, 193511b41d2SMark Murray const char *proxy_command) 194511b41d2SMark Murray { 195511b41d2SMark Murray int gaierr; 196ca3176e7SBrian Feldman int on = 1; 197ca3176e7SBrian Feldman int sock = -1, attempt; 198ca3176e7SBrian Feldman char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 199ca3176e7SBrian Feldman struct addrinfo hints, *ai, *aitop; 200511b41d2SMark Murray struct linger linger; 201ca3176e7SBrian Feldman struct servent *sp; 202511b41d2SMark Murray 203c2d3a559SKris Kennaway debug("ssh_connect: getuid %u geteuid %u anon %d", 204c2d3a559SKris Kennaway (u_int) getuid(), (u_int) geteuid(), anonymous); 205511b41d2SMark Murray 206511b41d2SMark Murray /* Get default port if port has not been set. */ 207511b41d2SMark Murray if (port == 0) { 208511b41d2SMark Murray sp = getservbyname(SSH_SERVICE_NAME, "tcp"); 209511b41d2SMark Murray if (sp) 210511b41d2SMark Murray port = ntohs(sp->s_port); 211511b41d2SMark Murray else 212511b41d2SMark Murray port = SSH_DEFAULT_PORT; 213511b41d2SMark Murray } 214511b41d2SMark Murray /* If a proxy command is given, connect using it. */ 215511b41d2SMark Murray if (proxy_command != NULL) 216ca3176e7SBrian Feldman return ssh_proxy_connect(host, port, pw, proxy_command); 217511b41d2SMark Murray 218511b41d2SMark Murray /* No proxy command. */ 219511b41d2SMark Murray 220511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 221511b41d2SMark Murray hints.ai_family = IPv4or6; 222511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 223511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 2241f5ce8f4SBrian Feldman if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 2251f5ce8f4SBrian Feldman fatal("%s: %.100s: %s", __progname, host, 226511b41d2SMark Murray gai_strerror(gaierr)); 227511b41d2SMark Murray 228511b41d2SMark Murray /* 229511b41d2SMark Murray * Try to connect several times. On some machines, the first time 230511b41d2SMark Murray * will sometimes fail. In general socket code appears to behave 231511b41d2SMark Murray * quite magically on many machines. 232511b41d2SMark Murray */ 233511b41d2SMark Murray for (attempt = 0; attempt < connection_attempts; attempt++) { 234511b41d2SMark Murray if (attempt > 0) 235511b41d2SMark Murray debug("Trying again..."); 236511b41d2SMark Murray 237511b41d2SMark Murray /* Loop through addresses for this host, and try each one in 238511b41d2SMark Murray sequence until the connection succeeds. */ 239511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 240511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 241511b41d2SMark Murray continue; 242511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 243511b41d2SMark Murray ntop, sizeof(ntop), strport, sizeof(strport), 244511b41d2SMark Murray NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 245511b41d2SMark Murray error("ssh_connect: getnameinfo failed"); 246511b41d2SMark Murray continue; 247511b41d2SMark Murray } 248511b41d2SMark Murray debug("Connecting to %.200s [%.100s] port %s.", 2491f5ce8f4SBrian Feldman host, ntop, strport); 250511b41d2SMark Murray 251511b41d2SMark Murray /* Create a socket for connecting. */ 252ca3176e7SBrian Feldman sock = ssh_create_socket(pw, 253ca3176e7SBrian Feldman !anonymous && geteuid() == 0, 254511b41d2SMark Murray ai->ai_family); 255511b41d2SMark Murray if (sock < 0) 256511b41d2SMark Murray continue; 257511b41d2SMark Murray 258511b41d2SMark Murray /* Connect to the host. We use the user's uid in the 259511b41d2SMark Murray * hope that it will help with tcp_wrappers showing 260511b41d2SMark Murray * the remote uid as root. 261511b41d2SMark Murray */ 262ca3176e7SBrian Feldman temporarily_use_uid(pw); 263511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { 264511b41d2SMark Murray /* Successful connection. */ 265c322fe35SKris Kennaway memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 266511b41d2SMark Murray restore_uid(); 267511b41d2SMark Murray break; 268511b41d2SMark Murray } else { 269511b41d2SMark Murray debug("connect: %.100s", strerror(errno)); 270511b41d2SMark Murray restore_uid(); 271511b41d2SMark Murray /* 272511b41d2SMark Murray * Close the failed socket; there appear to 273511b41d2SMark Murray * be some problems when reusing a socket for 274511b41d2SMark Murray * which connect() has already returned an 275511b41d2SMark Murray * error. 276511b41d2SMark Murray */ 277511b41d2SMark Murray shutdown(sock, SHUT_RDWR); 278511b41d2SMark Murray close(sock); 279511b41d2SMark Murray } 280511b41d2SMark Murray } 2811f5ce8f4SBrian Feldman if (ai) 282511b41d2SMark Murray break; /* Successful connection. */ 283511b41d2SMark Murray 284511b41d2SMark Murray /* Sleep a moment before retrying. */ 285511b41d2SMark Murray sleep(1); 286511b41d2SMark Murray } 287511b41d2SMark Murray 288511b41d2SMark Murray freeaddrinfo(aitop); 289511b41d2SMark Murray 290511b41d2SMark Murray /* Return failure if we didn't get a successful connection. */ 291511b41d2SMark Murray if (attempt >= connection_attempts) 292511b41d2SMark Murray return 0; 293511b41d2SMark Murray 294511b41d2SMark Murray debug("Connection established."); 295511b41d2SMark Murray 296511b41d2SMark Murray /* 297511b41d2SMark Murray * Set socket options. We would like the socket to disappear as soon 298511b41d2SMark Murray * as it has been closed for whatever reason. 299511b41d2SMark Murray */ 300511b41d2SMark Murray /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ 301511b41d2SMark Murray linger.l_onoff = 1; 302511b41d2SMark Murray linger.l_linger = 5; 303511b41d2SMark Murray setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); 304511b41d2SMark Murray 305ca3176e7SBrian Feldman /* Set keepalives if requested. */ 306ca3176e7SBrian Feldman if (options.keepalives && 307ca3176e7SBrian Feldman setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, 308ca3176e7SBrian Feldman sizeof(on)) < 0) 309ca3176e7SBrian Feldman error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); 310ca3176e7SBrian Feldman 311511b41d2SMark Murray /* Set the connection. */ 312511b41d2SMark Murray packet_set_connection(sock, sock); 313511b41d2SMark Murray 314511b41d2SMark Murray return 1; 315511b41d2SMark Murray } 316511b41d2SMark Murray 317511b41d2SMark Murray /* 318e8aafc91SKris Kennaway * Waits for the server identification string, and sends our own 319e8aafc91SKris Kennaway * identification string. 320511b41d2SMark Murray */ 321511b41d2SMark Murray void 322ca3176e7SBrian Feldman ssh_exchange_identification(void) 323511b41d2SMark Murray { 324e8aafc91SKris Kennaway char buf[256], remote_version[256]; /* must be same size! */ 325e8aafc91SKris Kennaway int remote_major, remote_minor, i, mismatch; 326e8aafc91SKris Kennaway int connection_in = packet_get_connection_in(); 327e8aafc91SKris Kennaway int connection_out = packet_get_connection_out(); 328ca3176e7SBrian Feldman int minor1 = PROTOCOL_MINOR_1; 329511b41d2SMark Murray 330e8aafc91SKris Kennaway /* Read other side\'s version identification. */ 331c2d3a559SKris Kennaway for (;;) { 332e8aafc91SKris Kennaway for (i = 0; i < sizeof(buf) - 1; i++) { 333c2d3a559SKris Kennaway int len = atomicio(read, connection_in, &buf[i], 1); 334e8aafc91SKris Kennaway if (len < 0) 335e8aafc91SKris Kennaway fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); 336e8aafc91SKris Kennaway if (len != 1) 337e8aafc91SKris Kennaway fatal("ssh_exchange_identification: Connection closed by remote host"); 338e8aafc91SKris Kennaway if (buf[i] == '\r') { 339e8aafc91SKris Kennaway buf[i] = '\n'; 340e8aafc91SKris Kennaway buf[i + 1] = 0; 341e8aafc91SKris Kennaway continue; /**XXX wait for \n */ 342511b41d2SMark Murray } 343e8aafc91SKris Kennaway if (buf[i] == '\n') { 344e8aafc91SKris Kennaway buf[i + 1] = 0; 345511b41d2SMark Murray break; 346e8aafc91SKris Kennaway } 347e8aafc91SKris Kennaway } 348e8aafc91SKris Kennaway buf[sizeof(buf) - 1] = 0; 349c2d3a559SKris Kennaway if (strncmp(buf, "SSH-", 4) == 0) 350c2d3a559SKris Kennaway break; 351c2d3a559SKris Kennaway debug("ssh_exchange_identification: %s", buf); 352c2d3a559SKris Kennaway } 353e8aafc91SKris Kennaway server_version_string = xstrdup(buf); 354511b41d2SMark Murray 355511b41d2SMark Murray /* 356e8aafc91SKris Kennaway * Check that the versions match. In future this might accept 357e8aafc91SKris Kennaway * several versions and set appropriate flags to handle them. 358511b41d2SMark Murray */ 359e8aafc91SKris Kennaway if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 360e8aafc91SKris Kennaway &remote_major, &remote_minor, remote_version) != 3) 361e8aafc91SKris Kennaway fatal("Bad remote protocol version identification: '%.100s'", buf); 362e8aafc91SKris Kennaway debug("Remote protocol version %d.%d, remote software version %.100s", 363e8aafc91SKris Kennaway remote_major, remote_minor, remote_version); 364511b41d2SMark Murray 365e8aafc91SKris Kennaway compat_datafellows(remote_version); 366e8aafc91SKris Kennaway mismatch = 0; 367e8aafc91SKris Kennaway 368e8aafc91SKris Kennaway switch(remote_major) { 369e8aafc91SKris Kennaway case 1: 370e8aafc91SKris Kennaway if (remote_minor == 99 && 371e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) && 372e8aafc91SKris Kennaway !(options.protocol & SSH_PROTO_1_PREFERRED)) { 373e8aafc91SKris Kennaway enable_compat20(); 374511b41d2SMark Murray break; 375e8aafc91SKris Kennaway } 376e8aafc91SKris Kennaway if (!(options.protocol & SSH_PROTO_1)) { 377e8aafc91SKris Kennaway mismatch = 1; 378e8aafc91SKris Kennaway break; 379e8aafc91SKris Kennaway } 380e8aafc91SKris Kennaway if (remote_minor < 3) { 381e8aafc91SKris Kennaway fatal("Remote machine has too old SSH software version."); 382ca3176e7SBrian Feldman } else if (remote_minor == 3 || remote_minor == 4) { 383e8aafc91SKris Kennaway /* We speak 1.3, too. */ 384e8aafc91SKris Kennaway enable_compat13(); 385ca3176e7SBrian Feldman minor1 = 3; 386e8aafc91SKris Kennaway if (options.forward_agent) { 387e8aafc91SKris Kennaway log("Agent forwarding disabled for protocol 1.3"); 388e8aafc91SKris Kennaway options.forward_agent = 0; 389e8aafc91SKris Kennaway } 390e8aafc91SKris Kennaway } 391e8aafc91SKris Kennaway break; 392e8aafc91SKris Kennaway case 2: 393e8aafc91SKris Kennaway if (options.protocol & SSH_PROTO_2) { 394e8aafc91SKris Kennaway enable_compat20(); 395e8aafc91SKris Kennaway break; 396e8aafc91SKris Kennaway } 397e8aafc91SKris Kennaway /* FALLTHROUGH */ 398511b41d2SMark Murray default: 399e8aafc91SKris Kennaway mismatch = 1; 400e8aafc91SKris Kennaway break; 401511b41d2SMark Murray } 402e8aafc91SKris Kennaway if (mismatch) 403e8aafc91SKris Kennaway fatal("Protocol major versions differ: %d vs. %d", 404e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 405e8aafc91SKris Kennaway remote_major); 406e8aafc91SKris Kennaway if (compat20) 407e8aafc91SKris Kennaway packet_set_ssh2_format(); 408e8aafc91SKris Kennaway /* Send our own protocol version identification. */ 409e8aafc91SKris Kennaway snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 410e8aafc91SKris Kennaway compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 411ca3176e7SBrian Feldman compat20 ? PROTOCOL_MINOR_2 : minor1, 412e8aafc91SKris Kennaway SSH_VERSION); 413e8aafc91SKris Kennaway if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) 414e8aafc91SKris Kennaway fatal("write: %.100s", strerror(errno)); 415e8aafc91SKris Kennaway client_version_string = xstrdup(buf); 416e8aafc91SKris Kennaway chop(client_version_string); 417e8aafc91SKris Kennaway chop(server_version_string); 418e8aafc91SKris Kennaway debug("Local version string %.100s", client_version_string); 419511b41d2SMark Murray } 420511b41d2SMark Murray 421ca3176e7SBrian Feldman /* defaults to 'no' */ 422511b41d2SMark Murray int 423e8aafc91SKris Kennaway read_yes_or_no(const char *prompt, int defval) 424511b41d2SMark Murray { 425e8aafc91SKris Kennaway char buf[1024]; 426e8aafc91SKris Kennaway FILE *f; 427e8aafc91SKris Kennaway int retval = -1; 428511b41d2SMark Murray 429ca3176e7SBrian Feldman if (options.batch_mode) 430ca3176e7SBrian Feldman return 0; 431ca3176e7SBrian Feldman 432ca3176e7SBrian Feldman if (isatty(STDIN_FILENO)) 433e8aafc91SKris Kennaway f = stdin; 434e8aafc91SKris Kennaway else 435ca3176e7SBrian Feldman f = fopen(_PATH_TTY, "rw"); 436e8aafc91SKris Kennaway 437e8aafc91SKris Kennaway if (f == NULL) 438511b41d2SMark Murray return 0; 439511b41d2SMark Murray 440e8aafc91SKris Kennaway fflush(stdout); 441511b41d2SMark Murray 442e8aafc91SKris Kennaway while (1) { 443e8aafc91SKris Kennaway fprintf(stderr, "%s", prompt); 444e8aafc91SKris Kennaway if (fgets(buf, sizeof(buf), f) == NULL) { 445e8aafc91SKris Kennaway /* Print a newline (the prompt probably didn\'t have one). */ 446e8aafc91SKris Kennaway fprintf(stderr, "\n"); 447e8aafc91SKris Kennaway strlcpy(buf, "no", sizeof buf); 448511b41d2SMark Murray } 449e8aafc91SKris Kennaway /* Remove newline from response. */ 450e8aafc91SKris Kennaway if (strchr(buf, '\n')) 451e8aafc91SKris Kennaway *strchr(buf, '\n') = 0; 452e8aafc91SKris Kennaway 453e8aafc91SKris Kennaway if (buf[0] == 0) 454e8aafc91SKris Kennaway retval = defval; 455e8aafc91SKris Kennaway if (strcmp(buf, "yes") == 0) 456e8aafc91SKris Kennaway retval = 1; 45709958426SBrian Feldman else if (strcmp(buf, "no") == 0) 458e8aafc91SKris Kennaway retval = 0; 45909958426SBrian Feldman else 46009958426SBrian Feldman fprintf(stderr, "Please type 'yes' or 'no'.\n"); 461e8aafc91SKris Kennaway 462e8aafc91SKris Kennaway if (retval != -1) { 463e8aafc91SKris Kennaway if (f != stdin) 464e8aafc91SKris Kennaway fclose(f); 465e8aafc91SKris Kennaway return retval; 466511b41d2SMark Murray } 467511b41d2SMark Murray } 468511b41d2SMark Murray } 469511b41d2SMark Murray 470e8aafc91SKris Kennaway /* 471e8aafc91SKris Kennaway * check whether the supplied host key is valid, return only if ok. 472e8aafc91SKris Kennaway */ 473e8aafc91SKris Kennaway 474511b41d2SMark Murray void 475e8aafc91SKris Kennaway check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, 476e8aafc91SKris Kennaway const char *user_hostfile, const char *system_hostfile) 477511b41d2SMark Murray { 478e8aafc91SKris Kennaway Key *file_key; 479e8aafc91SKris Kennaway char *type = key_type(host_key); 480e8aafc91SKris Kennaway char *ip = NULL; 481ca3176e7SBrian Feldman char hostline[1000], *hostp, *fp; 482e8aafc91SKris Kennaway HostStatus host_status; 483e8aafc91SKris Kennaway HostStatus ip_status; 484e8aafc91SKris Kennaway int local = 0, host_ip_differ = 0; 485e8aafc91SKris Kennaway char ntop[NI_MAXHOST]; 486ca3176e7SBrian Feldman int host_line, ip_line; 487ca3176e7SBrian Feldman const char *host_file = NULL, *ip_file = NULL; 488511b41d2SMark Murray 489e8aafc91SKris Kennaway /* 490e8aafc91SKris Kennaway * Force accepting of the host key for loopback/localhost. The 491e8aafc91SKris Kennaway * problem is that if the home directory is NFS-mounted to multiple 492e8aafc91SKris Kennaway * machines, localhost will refer to a different machine in each of 493e8aafc91SKris Kennaway * them, and the user will get bogus HOST_CHANGED warnings. This 494e8aafc91SKris Kennaway * essentially disables host authentication for localhost; however, 495e8aafc91SKris Kennaway * this is probably not a real problem. 496e8aafc91SKris Kennaway */ 497e8aafc91SKris Kennaway /** hostaddr == 0! */ 498e8aafc91SKris Kennaway switch (hostaddr->sa_family) { 499e8aafc91SKris Kennaway case AF_INET: 500e8aafc91SKris Kennaway local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 501511b41d2SMark Murray break; 502e8aafc91SKris Kennaway case AF_INET6: 503e8aafc91SKris Kennaway local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 504511b41d2SMark Murray break; 505e8aafc91SKris Kennaway default: 506e8aafc91SKris Kennaway local = 0; 507511b41d2SMark Murray break; 508511b41d2SMark Murray } 509ca3176e7SBrian Feldman if (local && options.host_key_alias == NULL) { 510ca3176e7SBrian Feldman debug("Forcing accepting of host key for " 511ca3176e7SBrian Feldman "loopback/localhost."); 512e8aafc91SKris Kennaway return; 513511b41d2SMark Murray } 514511b41d2SMark Murray 515e8aafc91SKris Kennaway /* 516ca3176e7SBrian Feldman * We don't have the remote ip-address for connections 517ca3176e7SBrian Feldman * using a proxy command 518e8aafc91SKris Kennaway */ 519ca3176e7SBrian Feldman if (options.proxy_command == NULL) { 520e8aafc91SKris Kennaway if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop), 521e8aafc91SKris Kennaway NULL, 0, NI_NUMERICHOST) != 0) 522e8aafc91SKris Kennaway fatal("check_host_key: getnameinfo failed"); 523e8aafc91SKris Kennaway ip = xstrdup(ntop); 524ca3176e7SBrian Feldman } else { 525ca3176e7SBrian Feldman ip = xstrdup("<no hostip for proxy command>"); 526ca3176e7SBrian Feldman } 527ca3176e7SBrian Feldman /* 528ca3176e7SBrian Feldman * Turn off check_host_ip if the connection is to localhost, via proxy 529ca3176e7SBrian Feldman * command or if we don't have a hostname to compare with 530ca3176e7SBrian Feldman */ 531ca3176e7SBrian Feldman if (options.check_host_ip && 532ca3176e7SBrian Feldman (local || strcmp(host, ip) == 0 || options.proxy_command != NULL)) 533ca3176e7SBrian Feldman options.check_host_ip = 0; 534ca3176e7SBrian Feldman 535ca3176e7SBrian Feldman /* 536ca3176e7SBrian Feldman * Allow the user to record the key under a different name. This is 537ca3176e7SBrian Feldman * useful for ssh tunneling over forwarded connections or if you run 538ca3176e7SBrian Feldman * multiple sshd's on different ports on the same machine. 539ca3176e7SBrian Feldman */ 540ca3176e7SBrian Feldman if (options.host_key_alias != NULL) { 541ca3176e7SBrian Feldman host = options.host_key_alias; 542ca3176e7SBrian Feldman debug("using hostkeyalias: %s", host); 543e8aafc91SKris Kennaway } 544e8aafc91SKris Kennaway 545e8aafc91SKris Kennaway /* 546e8aafc91SKris Kennaway * Store the host key from the known host file in here so that we can 547e8aafc91SKris Kennaway * compare it with the key for the IP address. 548e8aafc91SKris Kennaway */ 549e8aafc91SKris Kennaway file_key = key_new(host_key->type); 550e8aafc91SKris Kennaway 551e8aafc91SKris Kennaway /* 552e8aafc91SKris Kennaway * Check if the host key is present in the user\'s list of known 553e8aafc91SKris Kennaway * hosts or in the systemwide list. 554e8aafc91SKris Kennaway */ 555ca3176e7SBrian Feldman host_file = user_hostfile; 556ca3176e7SBrian Feldman host_status = check_host_in_hostfile(host_file, host, host_key, file_key, &host_line); 557ca3176e7SBrian Feldman if (host_status == HOST_NEW) { 558ca3176e7SBrian Feldman host_file = system_hostfile; 559ca3176e7SBrian Feldman host_status = check_host_in_hostfile(host_file, host, host_key, file_key, &host_line); 560ca3176e7SBrian Feldman } 561e8aafc91SKris Kennaway /* 562e8aafc91SKris Kennaway * Also perform check for the ip address, skip the check if we are 563e8aafc91SKris Kennaway * localhost or the hostname was an ip address to begin with 564e8aafc91SKris Kennaway */ 565ca3176e7SBrian Feldman if (options.check_host_ip) { 566e8aafc91SKris Kennaway Key *ip_key = key_new(host_key->type); 567e8aafc91SKris Kennaway 568ca3176e7SBrian Feldman ip_file = user_hostfile; 569ca3176e7SBrian Feldman ip_status = check_host_in_hostfile(ip_file, ip, host_key, ip_key, &ip_line); 570ca3176e7SBrian Feldman if (ip_status == HOST_NEW) { 571ca3176e7SBrian Feldman ip_file = system_hostfile; 572ca3176e7SBrian Feldman ip_status = check_host_in_hostfile(ip_file, ip, host_key, ip_key, &ip_line); 573ca3176e7SBrian Feldman } 574e8aafc91SKris Kennaway if (host_status == HOST_CHANGED && 575e8aafc91SKris Kennaway (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) 576e8aafc91SKris Kennaway host_ip_differ = 1; 577e8aafc91SKris Kennaway 578e8aafc91SKris Kennaway key_free(ip_key); 579e8aafc91SKris Kennaway } else 580e8aafc91SKris Kennaway ip_status = host_status; 581e8aafc91SKris Kennaway 582e8aafc91SKris Kennaway key_free(file_key); 583e8aafc91SKris Kennaway 584e8aafc91SKris Kennaway switch (host_status) { 585e8aafc91SKris Kennaway case HOST_OK: 586e8aafc91SKris Kennaway /* The host is known and the key matches. */ 587e8aafc91SKris Kennaway debug("Host '%.200s' is known and matches the %s host key.", 588e8aafc91SKris Kennaway host, type); 589ca3176e7SBrian Feldman debug("Found key in %s:%d", host_file, host_line); 590ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 591e8aafc91SKris Kennaway if (!add_host_to_hostfile(user_hostfile, ip, host_key)) 592ca3176e7SBrian Feldman log("Failed to add the %s host key for IP address '%.128s' to the list of known hosts (%.30s).", 593e8aafc91SKris Kennaway type, ip, user_hostfile); 594e8aafc91SKris Kennaway else 595ca3176e7SBrian Feldman log("Warning: Permanently added the %s host key for IP address '%.128s' to the list of known hosts.", 596e8aafc91SKris Kennaway type, ip); 597e8aafc91SKris Kennaway } 598e8aafc91SKris Kennaway break; 599e8aafc91SKris Kennaway case HOST_NEW: 600e8aafc91SKris Kennaway /* The host is new. */ 601e8aafc91SKris Kennaway if (options.strict_host_key_checking == 1) { 602e8aafc91SKris Kennaway /* User has requested strict host key checking. We will not add the host key 603e8aafc91SKris Kennaway automatically. The only alternative left is to abort. */ 604e8aafc91SKris Kennaway fatal("No %s host key is known for %.200s and you have requested strict checking.", type, host); 605e8aafc91SKris Kennaway } else if (options.strict_host_key_checking == 2) { 606e8aafc91SKris Kennaway /* The default */ 607e8aafc91SKris Kennaway char prompt[1024]; 608ca3176e7SBrian Feldman fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 609e8aafc91SKris Kennaway snprintf(prompt, sizeof(prompt), 610ca3176e7SBrian Feldman "The authenticity of host '%.200s (%s)' can't be established.\n" 611e8aafc91SKris Kennaway "%s key fingerprint is %s.\n" 612e8aafc91SKris Kennaway "Are you sure you want to continue connecting (yes/no)? ", 613ca3176e7SBrian Feldman host, ip, type, fp); 614ca3176e7SBrian Feldman xfree(fp); 615e8aafc91SKris Kennaway if (!read_yes_or_no(prompt, -1)) 616ca3176e7SBrian Feldman fatal("Aborted by user!"); 617e8aafc91SKris Kennaway } 618ca3176e7SBrian Feldman if (options.check_host_ip && ip_status == HOST_NEW) { 619e8aafc91SKris Kennaway snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 620e8aafc91SKris Kennaway hostp = hostline; 621e8aafc91SKris Kennaway } else 622e8aafc91SKris Kennaway hostp = host; 623e8aafc91SKris Kennaway 624e8aafc91SKris Kennaway /* If not in strict mode, add the key automatically to the local known_hosts file. */ 625e8aafc91SKris Kennaway if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) 626e8aafc91SKris Kennaway log("Failed to add the host to the list of known hosts (%.500s).", 627e8aafc91SKris Kennaway user_hostfile); 628e8aafc91SKris Kennaway else 629e8aafc91SKris Kennaway log("Warning: Permanently added '%.200s' (%s) to the list of known hosts.", 630e8aafc91SKris Kennaway hostp, type); 631e8aafc91SKris Kennaway break; 632e8aafc91SKris Kennaway case HOST_CHANGED: 633e8aafc91SKris Kennaway if (options.check_host_ip && host_ip_differ) { 634e8aafc91SKris Kennaway char *msg; 635e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 636e8aafc91SKris Kennaway msg = "is unknown"; 637e8aafc91SKris Kennaway else if (ip_status == HOST_OK) 638e8aafc91SKris Kennaway msg = "is unchanged"; 639e8aafc91SKris Kennaway else 640e8aafc91SKris Kennaway msg = "has a different value"; 641e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 642e8aafc91SKris Kennaway error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 643e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 644e8aafc91SKris Kennaway error("The %s host key for %s has changed,", type, host); 645e8aafc91SKris Kennaway error("and the key for the according IP address %s", ip); 646e8aafc91SKris Kennaway error("%s. This could either mean that", msg); 647e8aafc91SKris Kennaway error("DNS SPOOFING is happening or the IP address for the host"); 648ca3176e7SBrian Feldman error("and its host key have changed at the same time."); 649ca3176e7SBrian Feldman if (ip_status != HOST_NEW) 650ca3176e7SBrian Feldman error("Offending key for IP in %s:%d", ip_file, ip_line); 651e8aafc91SKris Kennaway } 652e8aafc91SKris Kennaway /* The host key has changed. */ 653ca3176e7SBrian Feldman fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); 654e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 655e8aafc91SKris Kennaway error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 656e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 657e8aafc91SKris Kennaway error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 658e8aafc91SKris Kennaway error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 659e8aafc91SKris Kennaway error("It is also possible that the %s host key has just been changed.", type); 660ca3176e7SBrian Feldman error("The fingerprint for the %s key sent by the remote host is\n%s.", 661ca3176e7SBrian Feldman type, fp); 662e8aafc91SKris Kennaway error("Please contact your system administrator."); 663e8aafc91SKris Kennaway error("Add correct host key in %.100s to get rid of this message.", 664e8aafc91SKris Kennaway user_hostfile); 665ca3176e7SBrian Feldman error("Offending key in %s:%d", host_file, host_line); 666ca3176e7SBrian Feldman xfree(fp); 667e8aafc91SKris Kennaway 668e8aafc91SKris Kennaway /* 669e8aafc91SKris Kennaway * If strict host key checking is in use, the user will have 670e8aafc91SKris Kennaway * to edit the key manually and we can only abort. 671e8aafc91SKris Kennaway */ 672e8aafc91SKris Kennaway if (options.strict_host_key_checking) 673e8aafc91SKris Kennaway fatal("%s host key for %.200s has changed and you have requested strict checking.", type, host); 674e8aafc91SKris Kennaway 675e8aafc91SKris Kennaway /* 676e8aafc91SKris Kennaway * If strict host key checking has not been requested, allow 677e8aafc91SKris Kennaway * the connection but without password authentication or 678e8aafc91SKris Kennaway * agent forwarding. 679e8aafc91SKris Kennaway */ 680e8aafc91SKris Kennaway if (options.password_authentication) { 681e8aafc91SKris Kennaway error("Password authentication is disabled to avoid trojan horses."); 682e8aafc91SKris Kennaway options.password_authentication = 0; 683e8aafc91SKris Kennaway } 684e8aafc91SKris Kennaway if (options.forward_agent) { 685e8aafc91SKris Kennaway error("Agent forwarding is disabled to avoid trojan horses."); 686e8aafc91SKris Kennaway options.forward_agent = 0; 687e8aafc91SKris Kennaway } 688ca3176e7SBrian Feldman if (options.forward_x11) { 689ca3176e7SBrian Feldman error("X11 forwarding is disabled to avoid trojan horses."); 690ca3176e7SBrian Feldman options.forward_x11 = 0; 691ca3176e7SBrian Feldman } 692ca3176e7SBrian Feldman if (options.num_local_forwards > 0 || options.num_remote_forwards > 0) { 693ca3176e7SBrian Feldman error("Port forwarding is disabled to avoid trojan horses."); 694ca3176e7SBrian Feldman options.num_local_forwards = options.num_remote_forwards = 0; 695ca3176e7SBrian Feldman } 696e8aafc91SKris Kennaway /* 697e8aafc91SKris Kennaway * XXX Should permit the user to change to use the new id. 698e8aafc91SKris Kennaway * This could be done by converting the host key to an 699e8aafc91SKris Kennaway * identifying sentence, tell that the host identifies itself 700e8aafc91SKris Kennaway * by that sentence, and ask the user if he/she whishes to 701e8aafc91SKris Kennaway * accept the authentication. 702e8aafc91SKris Kennaway */ 703e8aafc91SKris Kennaway break; 704e8aafc91SKris Kennaway } 705ca3176e7SBrian Feldman 706ca3176e7SBrian Feldman if (options.check_host_ip && host_status != HOST_CHANGED && 707ca3176e7SBrian Feldman ip_status == HOST_CHANGED) { 708ca3176e7SBrian Feldman log("Warning: the %s host key for '%.200s' " 709ca3176e7SBrian Feldman "differs from the key for the IP address '%.128s'", 710ca3176e7SBrian Feldman type, host, ip); 711ca3176e7SBrian Feldman if (host_status == HOST_OK) 712ca3176e7SBrian Feldman log("Matching host key in %s:%d", host_file, host_line); 713ca3176e7SBrian Feldman log("Offending key for IP in %s:%d", ip_file, ip_line); 714ca3176e7SBrian Feldman if (options.strict_host_key_checking == 1) { 715ca3176e7SBrian Feldman fatal("Exiting, you have requested strict checking."); 716ca3176e7SBrian Feldman } else if (options.strict_host_key_checking == 2) { 717ca3176e7SBrian Feldman if (!read_yes_or_no("Are you sure you want " \ 718ca3176e7SBrian Feldman "to continue connecting (yes/no)? ", -1)) 719ca3176e7SBrian Feldman fatal("Aborted by user!"); 720ca3176e7SBrian Feldman } 721ca3176e7SBrian Feldman } 722ca3176e7SBrian Feldman 723e8aafc91SKris Kennaway xfree(ip); 724e8aafc91SKris Kennaway } 725511b41d2SMark Murray 726fe5fd017SMark Murray #ifdef KRB5 727fe5fd017SMark Murray int 728fe5fd017SMark Murray try_krb5_authentication(krb5_context *context, krb5_auth_context *auth_context) 729fe5fd017SMark Murray { 730fe5fd017SMark Murray krb5_error_code problem; 731fe5fd017SMark Murray const char *tkfile; 732fe5fd017SMark Murray struct stat buf; 733fe5fd017SMark Murray krb5_ccache ccache = NULL; 734fe5fd017SMark Murray const char *remotehost; 735fe5fd017SMark Murray krb5_data ap; 736fe5fd017SMark Murray int type, payload_len; 737fe5fd017SMark Murray krb5_ap_rep_enc_part *reply = NULL; 738fe5fd017SMark Murray int ret; 739fe5fd017SMark Murray 740fe5fd017SMark Murray memset(&ap, 0, sizeof(ap)); 741fe5fd017SMark Murray 742fe5fd017SMark Murray problem = krb5_init_context(context); 743fe5fd017SMark Murray if (problem) { 744fe5fd017SMark Murray ret = 0; 745fe5fd017SMark Murray goto out; 746fe5fd017SMark Murray } 747fe5fd017SMark Murray 748fe5fd017SMark Murray tkfile = krb5_cc_default_name(*context); 749fe5fd017SMark Murray if (strncmp(tkfile, "FILE:", 5) == 0) 750fe5fd017SMark Murray tkfile += 5; 751fe5fd017SMark Murray 752fe5fd017SMark Murray if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) { 753fe5fd017SMark Murray debug("Kerberos V5: could not get default ccache (permission denied)."); 754fe5fd017SMark Murray ret = 0; 755fe5fd017SMark Murray goto out; 756fe5fd017SMark Murray } 757fe5fd017SMark Murray 758fe5fd017SMark Murray problem = krb5_cc_default(*context, &ccache); 759fe5fd017SMark Murray if (problem) { 760fe5fd017SMark Murray ret = 0; 761fe5fd017SMark Murray goto out; 762fe5fd017SMark Murray } 763fe5fd017SMark Murray 764fe5fd017SMark Murray remotehost = get_canonical_hostname(); 765fe5fd017SMark Murray 766aeccfe99SAssar Westerlund problem = krb5_mk_req(*context, auth_context, AP_OPTS_MUTUAL_REQUIRED, 767aeccfe99SAssar Westerlund "host", remotehost, NULL, ccache, &ap); 768fe5fd017SMark Murray if (problem) { 769fe5fd017SMark Murray ret = 0; 770fe5fd017SMark Murray goto out; 771fe5fd017SMark Murray } 772fe5fd017SMark Murray 773cb96ab36SAssar Westerlund packet_start(SSH_CMSG_AUTH_KERBEROS); 774fe5fd017SMark Murray packet_put_string((char *) ap.data, ap.length); 775fe5fd017SMark Murray packet_send(); 776fe5fd017SMark Murray packet_write_wait(); 777fe5fd017SMark Murray 778fe5fd017SMark Murray xfree(ap.data); 779fe5fd017SMark Murray ap.length = 0; 780fe5fd017SMark Murray 781fe5fd017SMark Murray type = packet_read(&payload_len); 782fe5fd017SMark Murray switch (type) { 783fe5fd017SMark Murray case SSH_SMSG_FAILURE: 784cb96ab36SAssar Westerlund /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ 785fe5fd017SMark Murray debug("Kerberos V5 authentication failed."); 786fe5fd017SMark Murray ret = 0; 787fe5fd017SMark Murray break; 788fe5fd017SMark Murray 789cb96ab36SAssar Westerlund case SSH_SMSG_AUTH_KERBEROS_RESPONSE: 790cb96ab36SAssar Westerlund /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ 791fe5fd017SMark Murray debug("Kerberos V5 authentication accepted."); 792fe5fd017SMark Murray 793fe5fd017SMark Murray /* Get server's response. */ 794fe5fd017SMark Murray ap.data = packet_get_string((unsigned int *) &ap.length); 795fe5fd017SMark Murray 796fe5fd017SMark Murray packet_integrity_check(payload_len, 4 + ap.length, type); 797fe5fd017SMark Murray /* XXX je to dobre? */ 798fe5fd017SMark Murray 799fe5fd017SMark Murray problem = krb5_rd_rep(*context, *auth_context, &ap, &reply); 800fe5fd017SMark Murray if (problem) { 801fe5fd017SMark Murray ret = 0; 802fe5fd017SMark Murray } 803fe5fd017SMark Murray ret = 1; 804fe5fd017SMark Murray break; 805fe5fd017SMark Murray 806fe5fd017SMark Murray default: 807fe5fd017SMark Murray packet_disconnect("Protocol error on Kerberos V5 response: %d", type); 808fe5fd017SMark Murray ret = 0; 809fe5fd017SMark Murray break; 810fe5fd017SMark Murray 811fe5fd017SMark Murray } 812fe5fd017SMark Murray 813fe5fd017SMark Murray out: 814fe5fd017SMark Murray if (ccache != NULL) 815fe5fd017SMark Murray krb5_cc_close(*context, ccache); 816fe5fd017SMark Murray if (reply != NULL) 817fe5fd017SMark Murray krb5_free_ap_rep_enc_part(*context, reply); 818fe5fd017SMark Murray if (ap.length > 0) 819fe5fd017SMark Murray krb5_data_free(&ap); 820fe5fd017SMark Murray 821fe5fd017SMark Murray return ret; 822fe5fd017SMark Murray 823fe5fd017SMark Murray } 824fe5fd017SMark Murray 825fe5fd017SMark Murray void 826fe5fd017SMark Murray send_krb5_tgt(krb5_context context, krb5_auth_context auth_context) 827fe5fd017SMark Murray { 828fe5fd017SMark Murray int fd; 829fe5fd017SMark Murray int type, payload_len; 830fe5fd017SMark Murray krb5_error_code problem; 831fe5fd017SMark Murray krb5_data outbuf; 832fe5fd017SMark Murray krb5_ccache ccache = NULL; 833fe5fd017SMark Murray krb5_creds creds; 834fe5fd017SMark Murray krb5_kdc_flags flags; 835fe5fd017SMark Murray const char* remotehost = get_canonical_hostname(); 836fe5fd017SMark Murray 837fe5fd017SMark Murray memset(&creds, 0, sizeof(creds)); 838fe5fd017SMark Murray memset(&outbuf, 0, sizeof(outbuf)); 839fe5fd017SMark Murray 840fe5fd017SMark Murray fd = packet_get_connection_in(); 841fe5fd017SMark Murray problem = krb5_auth_con_setaddrs_from_fd(context, auth_context, &fd); 842fe5fd017SMark Murray if (problem) { 843fe5fd017SMark Murray goto out; 844fe5fd017SMark Murray } 845fe5fd017SMark Murray 846fe5fd017SMark Murray #if 0 847fe5fd017SMark Murray tkfile = krb5_cc_default_name(context); 848fe5fd017SMark Murray if (strncmp(tkfile, "FILE:", 5) == 0) 849fe5fd017SMark Murray tkfile += 5; 850fe5fd017SMark Murray 851fe5fd017SMark Murray if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) { 852fe5fd017SMark Murray debug("Kerberos V5: could not get default ccache (permission denied)."); 853fe5fd017SMark Murray goto out; 854fe5fd017SMark Murray } 855fe5fd017SMark Murray #endif 856fe5fd017SMark Murray 857fe5fd017SMark Murray problem = krb5_cc_default(context, &ccache); 858fe5fd017SMark Murray if (problem) { 859fe5fd017SMark Murray goto out; 860fe5fd017SMark Murray } 861fe5fd017SMark Murray 862fe5fd017SMark Murray problem = krb5_cc_get_principal(context, ccache, &creds.client); 863fe5fd017SMark Murray if (problem) { 864fe5fd017SMark Murray goto out; 865fe5fd017SMark Murray } 866fe5fd017SMark Murray 867fe5fd017SMark Murray problem = krb5_build_principal(context, &creds.server, 868fe5fd017SMark Murray strlen(creds.client->realm), 869fe5fd017SMark Murray creds.client->realm, 870fe5fd017SMark Murray "krbtgt", 871fe5fd017SMark Murray creds.client->realm, 872fe5fd017SMark Murray NULL); 873fe5fd017SMark Murray if (problem) { 874fe5fd017SMark Murray goto out; 875fe5fd017SMark Murray } 876fe5fd017SMark Murray 877fe5fd017SMark Murray creds.times.endtime = 0; 878fe5fd017SMark Murray 879fe5fd017SMark Murray flags.i = 0; 880fe5fd017SMark Murray flags.b.forwarded = 1; 881fe5fd017SMark Murray flags.b.forwardable = krb5_config_get_bool(context, NULL, 882fe5fd017SMark Murray "libdefaults", "forwardable", NULL); 883fe5fd017SMark Murray 884fe5fd017SMark Murray problem = krb5_get_forwarded_creds (context, 885fe5fd017SMark Murray auth_context, 886fe5fd017SMark Murray ccache, 887fe5fd017SMark Murray flags.i, 888fe5fd017SMark Murray remotehost, 889fe5fd017SMark Murray &creds, 890fe5fd017SMark Murray &outbuf); 891fe5fd017SMark Murray if (problem) { 892fe5fd017SMark Murray goto out; 893fe5fd017SMark Murray } 894fe5fd017SMark Murray 895cb96ab36SAssar Westerlund packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); 896fe5fd017SMark Murray packet_put_string((char *)outbuf.data, outbuf.length); 897fe5fd017SMark Murray packet_send(); 898fe5fd017SMark Murray packet_write_wait(); 899fe5fd017SMark Murray 900fe5fd017SMark Murray type = packet_read(&payload_len); 901fe5fd017SMark Murray switch (type) { 902fe5fd017SMark Murray case SSH_SMSG_SUCCESS: 903fe5fd017SMark Murray break; 904fe5fd017SMark Murray case SSH_SMSG_FAILURE: 905fe5fd017SMark Murray break; 906fe5fd017SMark Murray default: 907fe5fd017SMark Murray break; 908fe5fd017SMark Murray } 909fe5fd017SMark Murray 910fe5fd017SMark Murray out: 911fe5fd017SMark Murray if (creds.client) 912fe5fd017SMark Murray krb5_free_principal(context, creds.client); 913fe5fd017SMark Murray if (creds.server) 914fe5fd017SMark Murray krb5_free_principal(context, creds.server); 915fe5fd017SMark Murray if (ccache) 916fe5fd017SMark Murray krb5_cc_close(context, ccache); 917fe5fd017SMark Murray if (outbuf.data) 918fe5fd017SMark Murray xfree(outbuf.data); 919fe5fd017SMark Murray 920fe5fd017SMark Murray return; 921fe5fd017SMark Murray } 922fe5fd017SMark Murray #endif /* KRB5 */ 923fe5fd017SMark Murray 924511b41d2SMark Murray /* 925511b41d2SMark Murray * Starts a dialog with the server, and authenticates the current user on the 926511b41d2SMark Murray * server. This does not need any extra privileges. The basic connection 927511b41d2SMark Murray * to the server must already have been established before this is called. 928511b41d2SMark Murray * If login fails, this function prints an error and never returns. 929511b41d2SMark Murray * This function does not require super-user privileges. 930511b41d2SMark Murray */ 931511b41d2SMark Murray void 932ca3176e7SBrian Feldman ssh_login(Key **keys, int nkeys, const char *orighost, 933ca3176e7SBrian Feldman struct sockaddr *hostaddr, struct passwd *pw) 934511b41d2SMark Murray { 935511b41d2SMark Murray char *host, *cp; 936e8aafc91SKris Kennaway char *server_user, *local_user; 937e8aafc91SKris Kennaway 938e8aafc91SKris Kennaway local_user = xstrdup(pw->pw_name); 939e8aafc91SKris Kennaway server_user = options.user ? options.user : local_user; 940511b41d2SMark Murray 941511b41d2SMark Murray /* Convert the user-supplied hostname into all lowercase. */ 942511b41d2SMark Murray host = xstrdup(orighost); 943511b41d2SMark Murray for (cp = host; *cp; cp++) 944511b41d2SMark Murray if (isupper(*cp)) 945511b41d2SMark Murray *cp = tolower(*cp); 946511b41d2SMark Murray 947511b41d2SMark Murray /* Exchange protocol version identification strings with the server. */ 948511b41d2SMark Murray ssh_exchange_identification(); 949511b41d2SMark Murray 950511b41d2SMark Murray /* Put the connection into non-blocking mode. */ 951511b41d2SMark Murray packet_set_nonblocking(); 952511b41d2SMark Murray 953511b41d2SMark Murray /* key exchange */ 954511b41d2SMark Murray /* authenticate user */ 955e8aafc91SKris Kennaway if (compat20) { 956e8aafc91SKris Kennaway ssh_kex2(host, hostaddr); 957ca3176e7SBrian Feldman ssh_userauth2(local_user, server_user, host, keys, nkeys); 958e8aafc91SKris Kennaway } else { 959e8aafc91SKris Kennaway ssh_kex(host, hostaddr); 960ca3176e7SBrian Feldman ssh_userauth1(local_user, server_user, host, keys, nkeys); 961e8aafc91SKris Kennaway } 962511b41d2SMark Murray } 963e0fbb1d2SBrian Feldman 964e0fbb1d2SBrian Feldman void 965e0fbb1d2SBrian Feldman ssh_put_password(char *password) 966e0fbb1d2SBrian Feldman { 967e0fbb1d2SBrian Feldman int size; 968e0fbb1d2SBrian Feldman char *padded; 969e0fbb1d2SBrian Feldman 970ca3176e7SBrian Feldman if (datafellows & SSH_BUG_PASSWORDPAD) { 971ca3176e7SBrian Feldman packet_put_string(password, strlen(password)); 972ca3176e7SBrian Feldman return; 973ca3176e7SBrian Feldman } 974e0fbb1d2SBrian Feldman size = roundup(strlen(password) + 1, 32); 975e0fbb1d2SBrian Feldman padded = xmalloc(size); 976e0fbb1d2SBrian Feldman memset(padded, 0, size); 977e0fbb1d2SBrian Feldman strlcpy(padded, password, size); 978e0fbb1d2SBrian Feldman packet_put_string(padded, size); 979e0fbb1d2SBrian Feldman memset(padded, 0, size); 980e0fbb1d2SBrian Feldman xfree(padded); 981e0fbb1d2SBrian Feldman } 982