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" 1609958426SBrian Feldman RCSID("$OpenBSD: sshconnect.c,v 1.79 2000/09/17 15:52:51 markus Exp $"); 17c2d3a559SKris Kennaway RCSID("$FreeBSD$"); 18511b41d2SMark Murray 1918a71195SBrian Feldman #include <openssl/bn.h> 20e8aafc91SKris Kennaway #include <openssl/dsa.h> 21e8aafc91SKris Kennaway #include <openssl/rsa.h> 22e8aafc91SKris Kennaway 23511b41d2SMark Murray #include "xmalloc.h" 24511b41d2SMark Murray #include "rsa.h" 25511b41d2SMark Murray #include "ssh.h" 26e8aafc91SKris Kennaway #include "buffer.h" 27511b41d2SMark Murray #include "packet.h" 28511b41d2SMark Murray #include "uidswap.h" 29511b41d2SMark Murray #include "compat.h" 30511b41d2SMark Murray #include "readconf.h" 313c6ae118SKris Kennaway #include "key.h" 32e8aafc91SKris Kennaway #include "sshconnect.h" 333c6ae118SKris Kennaway #include "hostfile.h" 34511b41d2SMark Murray 35e8aafc91SKris Kennaway char *client_version_string = NULL; 36e8aafc91SKris Kennaway char *server_version_string = NULL; 37511b41d2SMark Murray 38511b41d2SMark Murray extern Options options; 39511b41d2SMark Murray extern char *__progname; 40511b41d2SMark Murray 41511b41d2SMark Murray /* 42511b41d2SMark Murray * Connect to the given ssh server using a proxy command. 43511b41d2SMark Murray */ 44511b41d2SMark Murray int 45511b41d2SMark Murray ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, 46511b41d2SMark Murray const char *proxy_command) 47511b41d2SMark Murray { 48511b41d2SMark Murray Buffer command; 49511b41d2SMark Murray const char *cp; 50511b41d2SMark Murray char *command_string; 51511b41d2SMark Murray int pin[2], pout[2]; 52e8aafc91SKris Kennaway pid_t pid; 53511b41d2SMark Murray char strport[NI_MAXSERV]; 54511b41d2SMark Murray 55511b41d2SMark Murray /* Convert the port number into a string. */ 56511b41d2SMark Murray snprintf(strport, sizeof strport, "%hu", port); 57511b41d2SMark Murray 58511b41d2SMark Murray /* Build the final command string in the buffer by making the 59511b41d2SMark Murray appropriate substitutions to the given proxy command. */ 60511b41d2SMark Murray buffer_init(&command); 61511b41d2SMark Murray for (cp = proxy_command; *cp; cp++) { 62511b41d2SMark Murray if (cp[0] == '%' && cp[1] == '%') { 63511b41d2SMark Murray buffer_append(&command, "%", 1); 64511b41d2SMark Murray cp++; 65511b41d2SMark Murray continue; 66511b41d2SMark Murray } 67511b41d2SMark Murray if (cp[0] == '%' && cp[1] == 'h') { 68511b41d2SMark Murray buffer_append(&command, host, strlen(host)); 69511b41d2SMark Murray cp++; 70511b41d2SMark Murray continue; 71511b41d2SMark Murray } 72511b41d2SMark Murray if (cp[0] == '%' && cp[1] == 'p') { 73511b41d2SMark Murray buffer_append(&command, strport, strlen(strport)); 74511b41d2SMark Murray cp++; 75511b41d2SMark Murray continue; 76511b41d2SMark Murray } 77511b41d2SMark Murray buffer_append(&command, cp, 1); 78511b41d2SMark Murray } 79511b41d2SMark Murray buffer_append(&command, "\0", 1); 80511b41d2SMark Murray 81511b41d2SMark Murray /* Get the final command string. */ 82511b41d2SMark Murray command_string = buffer_ptr(&command); 83511b41d2SMark Murray 84511b41d2SMark Murray /* Create pipes for communicating with the proxy. */ 85511b41d2SMark Murray if (pipe(pin) < 0 || pipe(pout) < 0) 86511b41d2SMark Murray fatal("Could not create pipes to communicate with the proxy: %.100s", 87511b41d2SMark Murray strerror(errno)); 88511b41d2SMark Murray 89511b41d2SMark Murray debug("Executing proxy command: %.500s", command_string); 90511b41d2SMark Murray 91511b41d2SMark Murray /* Fork and execute the proxy command. */ 92511b41d2SMark Murray if ((pid = fork()) == 0) { 93511b41d2SMark Murray char *argv[10]; 94511b41d2SMark Murray 95511b41d2SMark Murray /* Child. Permanently give up superuser privileges. */ 96511b41d2SMark Murray permanently_set_uid(original_real_uid); 97511b41d2SMark Murray 98511b41d2SMark Murray /* Redirect stdin and stdout. */ 99511b41d2SMark Murray close(pin[1]); 100511b41d2SMark Murray if (pin[0] != 0) { 101511b41d2SMark Murray if (dup2(pin[0], 0) < 0) 102511b41d2SMark Murray perror("dup2 stdin"); 103511b41d2SMark Murray close(pin[0]); 104511b41d2SMark Murray } 105511b41d2SMark Murray close(pout[0]); 106511b41d2SMark Murray if (dup2(pout[1], 1) < 0) 107511b41d2SMark Murray perror("dup2 stdout"); 108511b41d2SMark Murray /* Cannot be 1 because pin allocated two descriptors. */ 109511b41d2SMark Murray close(pout[1]); 110511b41d2SMark Murray 111511b41d2SMark Murray /* Stderr is left as it is so that error messages get 112511b41d2SMark Murray printed on the user's terminal. */ 113511b41d2SMark Murray argv[0] = "/bin/sh"; 114511b41d2SMark Murray argv[1] = "-c"; 115511b41d2SMark Murray argv[2] = command_string; 116511b41d2SMark Murray argv[3] = NULL; 117511b41d2SMark Murray 118511b41d2SMark Murray /* Execute the proxy command. Note that we gave up any 119511b41d2SMark Murray extra privileges above. */ 120511b41d2SMark Murray execv("/bin/sh", argv); 121511b41d2SMark Murray perror("/bin/sh"); 122511b41d2SMark Murray exit(1); 123511b41d2SMark Murray } 124511b41d2SMark Murray /* Parent. */ 125511b41d2SMark Murray if (pid < 0) 126511b41d2SMark Murray fatal("fork failed: %.100s", strerror(errno)); 127511b41d2SMark Murray 128511b41d2SMark Murray /* Close child side of the descriptors. */ 129511b41d2SMark Murray close(pin[0]); 130511b41d2SMark Murray close(pout[1]); 131511b41d2SMark Murray 132511b41d2SMark Murray /* Free the command name. */ 133511b41d2SMark Murray buffer_free(&command); 134511b41d2SMark Murray 135511b41d2SMark Murray /* Set the connection file descriptors. */ 136511b41d2SMark Murray packet_set_connection(pout[0], pin[1]); 137511b41d2SMark Murray 138511b41d2SMark Murray return 1; 139511b41d2SMark Murray } 140511b41d2SMark Murray 141511b41d2SMark Murray /* 142511b41d2SMark Murray * Creates a (possibly privileged) socket for use as the ssh connection. 143511b41d2SMark Murray */ 144511b41d2SMark Murray int 145511b41d2SMark Murray ssh_create_socket(uid_t original_real_uid, int privileged, int family) 146511b41d2SMark Murray { 147511b41d2SMark Murray int sock; 148511b41d2SMark Murray 149511b41d2SMark Murray /* 150511b41d2SMark Murray * If we are running as root and want to connect to a privileged 151511b41d2SMark Murray * port, bind our own socket to a privileged port. 152511b41d2SMark Murray */ 153511b41d2SMark Murray if (privileged) { 154511b41d2SMark Murray int p = IPPORT_RESERVED - 1; 155511b41d2SMark Murray sock = rresvport_af(&p, family); 156511b41d2SMark Murray if (sock < 0) 157511b41d2SMark Murray error("rresvport: af=%d %.100s", family, strerror(errno)); 158511b41d2SMark Murray else 159511b41d2SMark Murray debug("Allocated local port %d.", p); 160511b41d2SMark Murray } else { 161511b41d2SMark Murray /* 162511b41d2SMark Murray * Just create an ordinary socket on arbitrary port. We use 163511b41d2SMark Murray * the user's uid to create the socket. 164511b41d2SMark Murray */ 165511b41d2SMark Murray temporarily_use_uid(original_real_uid); 166511b41d2SMark Murray sock = socket(family, SOCK_STREAM, 0); 167511b41d2SMark Murray if (sock < 0) 168511b41d2SMark Murray error("socket: %.100s", strerror(errno)); 169511b41d2SMark Murray restore_uid(); 170511b41d2SMark Murray } 171511b41d2SMark Murray return sock; 172511b41d2SMark Murray } 173511b41d2SMark Murray 174511b41d2SMark Murray /* 175511b41d2SMark Murray * Opens a TCP/IP connection to the remote server on the given host. 176511b41d2SMark Murray * The address of the remote host will be returned in hostaddr. 177511b41d2SMark Murray * If port is 0, the default port will be used. If anonymous is zero, 178511b41d2SMark Murray * a privileged port will be allocated to make the connection. 179511b41d2SMark Murray * This requires super-user privileges if anonymous is false. 180511b41d2SMark Murray * Connection_attempts specifies the maximum number of tries (one per 181511b41d2SMark Murray * second). If proxy_command is non-NULL, it specifies the command (with %h 182511b41d2SMark Murray * and %p substituted for host and port, respectively) to use to contact 183511b41d2SMark Murray * the daemon. 184511b41d2SMark Murray */ 185511b41d2SMark Murray int 1861f5ce8f4SBrian Feldman ssh_connect(const char *host, struct sockaddr_storage * hostaddr, 187511b41d2SMark Murray u_short port, int connection_attempts, 188511b41d2SMark Murray int anonymous, uid_t original_real_uid, 189511b41d2SMark Murray const char *proxy_command) 190511b41d2SMark Murray { 191511b41d2SMark Murray int sock = -1, attempt; 192511b41d2SMark Murray struct servent *sp; 193511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 194511b41d2SMark Murray char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 195511b41d2SMark Murray int gaierr; 196511b41d2SMark Murray struct linger linger; 197511b41d2SMark Murray 198c2d3a559SKris Kennaway debug("ssh_connect: getuid %u geteuid %u anon %d", 199c2d3a559SKris Kennaway (u_int) getuid(), (u_int) geteuid(), anonymous); 200511b41d2SMark Murray 201511b41d2SMark Murray /* Get default port if port has not been set. */ 202511b41d2SMark Murray if (port == 0) { 203511b41d2SMark Murray sp = getservbyname(SSH_SERVICE_NAME, "tcp"); 204511b41d2SMark Murray if (sp) 205511b41d2SMark Murray port = ntohs(sp->s_port); 206511b41d2SMark Murray else 207511b41d2SMark Murray port = SSH_DEFAULT_PORT; 208511b41d2SMark Murray } 209511b41d2SMark Murray /* If a proxy command is given, connect using it. */ 210511b41d2SMark Murray if (proxy_command != NULL) 2111f5ce8f4SBrian Feldman return ssh_proxy_connect(host, port, original_real_uid, proxy_command); 212511b41d2SMark Murray 213511b41d2SMark Murray /* No proxy command. */ 214511b41d2SMark Murray 215511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 216511b41d2SMark Murray hints.ai_family = IPv4or6; 217511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 218511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 2191f5ce8f4SBrian Feldman if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 2201f5ce8f4SBrian Feldman fatal("%s: %.100s: %s", __progname, host, 221511b41d2SMark Murray gai_strerror(gaierr)); 222511b41d2SMark Murray 223511b41d2SMark Murray /* 224511b41d2SMark Murray * Try to connect several times. On some machines, the first time 225511b41d2SMark Murray * will sometimes fail. In general socket code appears to behave 226511b41d2SMark Murray * quite magically on many machines. 227511b41d2SMark Murray */ 228511b41d2SMark Murray for (attempt = 0; attempt < connection_attempts; attempt++) { 229511b41d2SMark Murray if (attempt > 0) 230511b41d2SMark Murray debug("Trying again..."); 231511b41d2SMark Murray 232511b41d2SMark Murray /* Loop through addresses for this host, and try each one in 233511b41d2SMark Murray sequence until the connection succeeds. */ 234511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 235511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 236511b41d2SMark Murray continue; 237511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 238511b41d2SMark Murray ntop, sizeof(ntop), strport, sizeof(strport), 239511b41d2SMark Murray NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 240511b41d2SMark Murray error("ssh_connect: getnameinfo failed"); 241511b41d2SMark Murray continue; 242511b41d2SMark Murray } 243511b41d2SMark Murray debug("Connecting to %.200s [%.100s] port %s.", 2441f5ce8f4SBrian Feldman host, ntop, strport); 245511b41d2SMark Murray 246511b41d2SMark Murray /* Create a socket for connecting. */ 247511b41d2SMark Murray sock = ssh_create_socket(original_real_uid, 248511b41d2SMark Murray !anonymous && geteuid() == 0 && port < IPPORT_RESERVED, 249511b41d2SMark Murray ai->ai_family); 250511b41d2SMark Murray if (sock < 0) 251511b41d2SMark Murray continue; 252511b41d2SMark Murray 253511b41d2SMark Murray /* Connect to the host. We use the user's uid in the 254511b41d2SMark Murray * hope that it will help with tcp_wrappers showing 255511b41d2SMark Murray * the remote uid as root. 256511b41d2SMark Murray */ 257511b41d2SMark Murray temporarily_use_uid(original_real_uid); 258511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { 259511b41d2SMark Murray /* Successful connection. */ 260c322fe35SKris Kennaway memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 261511b41d2SMark Murray restore_uid(); 262511b41d2SMark Murray break; 263511b41d2SMark Murray } else { 264511b41d2SMark Murray debug("connect: %.100s", strerror(errno)); 265511b41d2SMark Murray restore_uid(); 266511b41d2SMark Murray /* 267511b41d2SMark Murray * Close the failed socket; there appear to 268511b41d2SMark Murray * be some problems when reusing a socket for 269511b41d2SMark Murray * which connect() has already returned an 270511b41d2SMark Murray * error. 271511b41d2SMark Murray */ 272511b41d2SMark Murray shutdown(sock, SHUT_RDWR); 273511b41d2SMark Murray close(sock); 274511b41d2SMark Murray } 275511b41d2SMark Murray } 2761f5ce8f4SBrian Feldman if (ai) 277511b41d2SMark Murray break; /* Successful connection. */ 278511b41d2SMark Murray 279511b41d2SMark Murray /* Sleep a moment before retrying. */ 280511b41d2SMark Murray sleep(1); 281511b41d2SMark Murray } 282511b41d2SMark Murray 283511b41d2SMark Murray freeaddrinfo(aitop); 284511b41d2SMark Murray 285511b41d2SMark Murray /* Return failure if we didn't get a successful connection. */ 286511b41d2SMark Murray if (attempt >= connection_attempts) 287511b41d2SMark Murray return 0; 288511b41d2SMark Murray 289511b41d2SMark Murray debug("Connection established."); 290511b41d2SMark Murray 291511b41d2SMark Murray /* 292511b41d2SMark Murray * Set socket options. We would like the socket to disappear as soon 293511b41d2SMark Murray * as it has been closed for whatever reason. 294511b41d2SMark Murray */ 295511b41d2SMark Murray /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ 296511b41d2SMark Murray linger.l_onoff = 1; 297511b41d2SMark Murray linger.l_linger = 5; 298511b41d2SMark Murray setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); 299511b41d2SMark Murray 300511b41d2SMark Murray /* Set the connection. */ 301511b41d2SMark Murray packet_set_connection(sock, sock); 302511b41d2SMark Murray 303511b41d2SMark Murray return 1; 304511b41d2SMark Murray } 305511b41d2SMark Murray 306511b41d2SMark Murray /* 307e8aafc91SKris Kennaway * Waits for the server identification string, and sends our own 308e8aafc91SKris Kennaway * identification string. 309511b41d2SMark Murray */ 310511b41d2SMark Murray void 311e8aafc91SKris Kennaway ssh_exchange_identification() 312511b41d2SMark Murray { 313e8aafc91SKris Kennaway char buf[256], remote_version[256]; /* must be same size! */ 314e8aafc91SKris Kennaway int remote_major, remote_minor, i, mismatch; 315e8aafc91SKris Kennaway int connection_in = packet_get_connection_in(); 316e8aafc91SKris Kennaway int connection_out = packet_get_connection_out(); 317511b41d2SMark Murray 318e8aafc91SKris Kennaway /* Read other side\'s version identification. */ 319c2d3a559SKris Kennaway for (;;) { 320e8aafc91SKris Kennaway for (i = 0; i < sizeof(buf) - 1; i++) { 321c2d3a559SKris Kennaway int len = atomicio(read, connection_in, &buf[i], 1); 322e8aafc91SKris Kennaway if (len < 0) 323e8aafc91SKris Kennaway fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); 324e8aafc91SKris Kennaway if (len != 1) 325e8aafc91SKris Kennaway fatal("ssh_exchange_identification: Connection closed by remote host"); 326e8aafc91SKris Kennaway if (buf[i] == '\r') { 327e8aafc91SKris Kennaway buf[i] = '\n'; 328e8aafc91SKris Kennaway buf[i + 1] = 0; 329e8aafc91SKris Kennaway continue; /**XXX wait for \n */ 330511b41d2SMark Murray } 331e8aafc91SKris Kennaway if (buf[i] == '\n') { 332e8aafc91SKris Kennaway buf[i + 1] = 0; 333511b41d2SMark Murray break; 334e8aafc91SKris Kennaway } 335e8aafc91SKris Kennaway } 336e8aafc91SKris Kennaway buf[sizeof(buf) - 1] = 0; 337c2d3a559SKris Kennaway if (strncmp(buf, "SSH-", 4) == 0) 338c2d3a559SKris Kennaway break; 339c2d3a559SKris Kennaway debug("ssh_exchange_identification: %s", buf); 340c2d3a559SKris Kennaway } 341e8aafc91SKris Kennaway server_version_string = xstrdup(buf); 342511b41d2SMark Murray 343511b41d2SMark Murray /* 344e8aafc91SKris Kennaway * Check that the versions match. In future this might accept 345e8aafc91SKris Kennaway * several versions and set appropriate flags to handle them. 346511b41d2SMark Murray */ 347e8aafc91SKris Kennaway if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 348e8aafc91SKris Kennaway &remote_major, &remote_minor, remote_version) != 3) 349e8aafc91SKris Kennaway fatal("Bad remote protocol version identification: '%.100s'", buf); 350e8aafc91SKris Kennaway debug("Remote protocol version %d.%d, remote software version %.100s", 351e8aafc91SKris Kennaway remote_major, remote_minor, remote_version); 352511b41d2SMark Murray 353e8aafc91SKris Kennaway compat_datafellows(remote_version); 354e8aafc91SKris Kennaway mismatch = 0; 355e8aafc91SKris Kennaway 356e8aafc91SKris Kennaway switch(remote_major) { 357e8aafc91SKris Kennaway case 1: 358e8aafc91SKris Kennaway if (remote_minor == 99 && 359e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) && 360e8aafc91SKris Kennaway !(options.protocol & SSH_PROTO_1_PREFERRED)) { 361e8aafc91SKris Kennaway enable_compat20(); 362511b41d2SMark Murray break; 363e8aafc91SKris Kennaway } 364e8aafc91SKris Kennaway if (!(options.protocol & SSH_PROTO_1)) { 365e8aafc91SKris Kennaway mismatch = 1; 366e8aafc91SKris Kennaway break; 367e8aafc91SKris Kennaway } 368e8aafc91SKris Kennaway if (remote_minor < 3) { 369e8aafc91SKris Kennaway fatal("Remote machine has too old SSH software version."); 370e8aafc91SKris Kennaway } else if (remote_minor == 3) { 371e8aafc91SKris Kennaway /* We speak 1.3, too. */ 372e8aafc91SKris Kennaway enable_compat13(); 373e8aafc91SKris Kennaway if (options.forward_agent) { 374e8aafc91SKris Kennaway log("Agent forwarding disabled for protocol 1.3"); 375e8aafc91SKris Kennaway options.forward_agent = 0; 376e8aafc91SKris Kennaway } 377e8aafc91SKris Kennaway } 378e8aafc91SKris Kennaway break; 379e8aafc91SKris Kennaway case 2: 380e8aafc91SKris Kennaway if (options.protocol & SSH_PROTO_2) { 381e8aafc91SKris Kennaway enable_compat20(); 382e8aafc91SKris Kennaway break; 383e8aafc91SKris Kennaway } 384e8aafc91SKris Kennaway /* FALLTHROUGH */ 385511b41d2SMark Murray default: 386e8aafc91SKris Kennaway mismatch = 1; 387e8aafc91SKris Kennaway break; 388511b41d2SMark Murray } 389e8aafc91SKris Kennaway if (mismatch) 390e8aafc91SKris Kennaway fatal("Protocol major versions differ: %d vs. %d", 391e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 392e8aafc91SKris Kennaway remote_major); 393e8aafc91SKris Kennaway if (compat20) 394e8aafc91SKris Kennaway packet_set_ssh2_format(); 395e8aafc91SKris Kennaway /* Send our own protocol version identification. */ 396e8aafc91SKris Kennaway snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 397e8aafc91SKris Kennaway compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 398e8aafc91SKris Kennaway compat20 ? PROTOCOL_MINOR_2 : PROTOCOL_MINOR_1, 399e8aafc91SKris Kennaway SSH_VERSION); 400e8aafc91SKris Kennaway if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) 401e8aafc91SKris Kennaway fatal("write: %.100s", strerror(errno)); 402e8aafc91SKris Kennaway client_version_string = xstrdup(buf); 403e8aafc91SKris Kennaway chop(client_version_string); 404e8aafc91SKris Kennaway chop(server_version_string); 405e8aafc91SKris Kennaway debug("Local version string %.100s", client_version_string); 406511b41d2SMark Murray } 407511b41d2SMark Murray 408511b41d2SMark Murray int 409e8aafc91SKris Kennaway read_yes_or_no(const char *prompt, int defval) 410511b41d2SMark Murray { 411e8aafc91SKris Kennaway char buf[1024]; 412e8aafc91SKris Kennaway FILE *f; 413e8aafc91SKris Kennaway int retval = -1; 414511b41d2SMark Murray 415e8aafc91SKris Kennaway if (isatty(0)) 416e8aafc91SKris Kennaway f = stdin; 417e8aafc91SKris Kennaway else 418e8aafc91SKris Kennaway f = fopen("/dev/tty", "rw"); 419e8aafc91SKris Kennaway 420e8aafc91SKris Kennaway if (f == NULL) 421511b41d2SMark Murray return 0; 422511b41d2SMark Murray 423e8aafc91SKris Kennaway fflush(stdout); 424511b41d2SMark Murray 425e8aafc91SKris Kennaway while (1) { 426e8aafc91SKris Kennaway fprintf(stderr, "%s", prompt); 427e8aafc91SKris Kennaway if (fgets(buf, sizeof(buf), f) == NULL) { 428e8aafc91SKris Kennaway /* Print a newline (the prompt probably didn\'t have one). */ 429e8aafc91SKris Kennaway fprintf(stderr, "\n"); 430e8aafc91SKris Kennaway strlcpy(buf, "no", sizeof buf); 431511b41d2SMark Murray } 432e8aafc91SKris Kennaway /* Remove newline from response. */ 433e8aafc91SKris Kennaway if (strchr(buf, '\n')) 434e8aafc91SKris Kennaway *strchr(buf, '\n') = 0; 435e8aafc91SKris Kennaway 436e8aafc91SKris Kennaway if (buf[0] == 0) 437e8aafc91SKris Kennaway retval = defval; 438e8aafc91SKris Kennaway if (strcmp(buf, "yes") == 0) 439e8aafc91SKris Kennaway retval = 1; 44009958426SBrian Feldman else if (strcmp(buf, "no") == 0) 441e8aafc91SKris Kennaway retval = 0; 44209958426SBrian Feldman else 44309958426SBrian Feldman fprintf(stderr, "Please type 'yes' or 'no'.\n"); 444e8aafc91SKris Kennaway 445e8aafc91SKris Kennaway if (retval != -1) { 446e8aafc91SKris Kennaway if (f != stdin) 447e8aafc91SKris Kennaway fclose(f); 448e8aafc91SKris Kennaway return retval; 449511b41d2SMark Murray } 450511b41d2SMark Murray } 451511b41d2SMark Murray } 452511b41d2SMark Murray 453e8aafc91SKris Kennaway /* 454e8aafc91SKris Kennaway * check whether the supplied host key is valid, return only if ok. 455e8aafc91SKris Kennaway */ 456e8aafc91SKris Kennaway 457511b41d2SMark Murray void 458e8aafc91SKris Kennaway check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, 459e8aafc91SKris Kennaway const char *user_hostfile, const char *system_hostfile) 460511b41d2SMark Murray { 461e8aafc91SKris Kennaway Key *file_key; 462e8aafc91SKris Kennaway char *type = key_type(host_key); 463e8aafc91SKris Kennaway char *ip = NULL; 464e8aafc91SKris Kennaway char hostline[1000], *hostp; 465e8aafc91SKris Kennaway HostStatus host_status; 466e8aafc91SKris Kennaway HostStatus ip_status; 467e8aafc91SKris Kennaway int local = 0, host_ip_differ = 0; 468e8aafc91SKris Kennaway char ntop[NI_MAXHOST]; 469511b41d2SMark Murray 470e8aafc91SKris Kennaway /* 471e8aafc91SKris Kennaway * Force accepting of the host key for loopback/localhost. The 472e8aafc91SKris Kennaway * problem is that if the home directory is NFS-mounted to multiple 473e8aafc91SKris Kennaway * machines, localhost will refer to a different machine in each of 474e8aafc91SKris Kennaway * them, and the user will get bogus HOST_CHANGED warnings. This 475e8aafc91SKris Kennaway * essentially disables host authentication for localhost; however, 476e8aafc91SKris Kennaway * this is probably not a real problem. 477e8aafc91SKris Kennaway */ 478e8aafc91SKris Kennaway /** hostaddr == 0! */ 479e8aafc91SKris Kennaway switch (hostaddr->sa_family) { 480e8aafc91SKris Kennaway case AF_INET: 481e8aafc91SKris Kennaway local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 482511b41d2SMark Murray break; 483e8aafc91SKris Kennaway case AF_INET6: 484e8aafc91SKris Kennaway local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 485511b41d2SMark Murray break; 486e8aafc91SKris Kennaway default: 487e8aafc91SKris Kennaway local = 0; 488511b41d2SMark Murray break; 489511b41d2SMark Murray } 490e8aafc91SKris Kennaway if (local) { 491e8aafc91SKris Kennaway debug("Forcing accepting of host key for loopback/localhost."); 492e8aafc91SKris Kennaway return; 493511b41d2SMark Murray } 494511b41d2SMark Murray 495e8aafc91SKris Kennaway /* 496e8aafc91SKris Kennaway * Turn off check_host_ip for proxy connects, since 497e8aafc91SKris Kennaway * we don't have the remote ip-address 498e8aafc91SKris Kennaway */ 499e8aafc91SKris Kennaway if (options.proxy_command != NULL && options.check_host_ip) 500e8aafc91SKris Kennaway options.check_host_ip = 0; 501e8aafc91SKris Kennaway 502e8aafc91SKris Kennaway if (options.check_host_ip) { 503e8aafc91SKris Kennaway if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop), 504e8aafc91SKris Kennaway NULL, 0, NI_NUMERICHOST) != 0) 505e8aafc91SKris Kennaway fatal("check_host_key: getnameinfo failed"); 506e8aafc91SKris Kennaway ip = xstrdup(ntop); 507e8aafc91SKris Kennaway } 508e8aafc91SKris Kennaway 509e8aafc91SKris Kennaway /* 510e8aafc91SKris Kennaway * Store the host key from the known host file in here so that we can 511e8aafc91SKris Kennaway * compare it with the key for the IP address. 512e8aafc91SKris Kennaway */ 513e8aafc91SKris Kennaway file_key = key_new(host_key->type); 514e8aafc91SKris Kennaway 515e8aafc91SKris Kennaway /* 516e8aafc91SKris Kennaway * Check if the host key is present in the user\'s list of known 517e8aafc91SKris Kennaway * hosts or in the systemwide list. 518e8aafc91SKris Kennaway */ 519e8aafc91SKris Kennaway host_status = check_host_in_hostfile(user_hostfile, host, host_key, file_key); 520e8aafc91SKris Kennaway if (host_status == HOST_NEW) 521e8aafc91SKris Kennaway host_status = check_host_in_hostfile(system_hostfile, host, host_key, file_key); 522e8aafc91SKris Kennaway /* 523e8aafc91SKris Kennaway * Also perform check for the ip address, skip the check if we are 524e8aafc91SKris Kennaway * localhost or the hostname was an ip address to begin with 525e8aafc91SKris Kennaway */ 526e8aafc91SKris Kennaway if (options.check_host_ip && !local && strcmp(host, ip)) { 527e8aafc91SKris Kennaway Key *ip_key = key_new(host_key->type); 528e8aafc91SKris Kennaway ip_status = check_host_in_hostfile(user_hostfile, ip, host_key, ip_key); 529e8aafc91SKris Kennaway 530e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 531e8aafc91SKris Kennaway ip_status = check_host_in_hostfile(system_hostfile, ip, host_key, ip_key); 532e8aafc91SKris Kennaway if (host_status == HOST_CHANGED && 533e8aafc91SKris Kennaway (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) 534e8aafc91SKris Kennaway host_ip_differ = 1; 535e8aafc91SKris Kennaway 536e8aafc91SKris Kennaway key_free(ip_key); 537e8aafc91SKris Kennaway } else 538e8aafc91SKris Kennaway ip_status = host_status; 539e8aafc91SKris Kennaway 540e8aafc91SKris Kennaway key_free(file_key); 541e8aafc91SKris Kennaway 542e8aafc91SKris Kennaway switch (host_status) { 543e8aafc91SKris Kennaway case HOST_OK: 544e8aafc91SKris Kennaway /* The host is known and the key matches. */ 545e8aafc91SKris Kennaway debug("Host '%.200s' is known and matches the %s host key.", 546e8aafc91SKris Kennaway host, type); 547e8aafc91SKris Kennaway if (options.check_host_ip) { 548e8aafc91SKris Kennaway if (ip_status == HOST_NEW) { 549e8aafc91SKris Kennaway if (!add_host_to_hostfile(user_hostfile, ip, host_key)) 550e8aafc91SKris Kennaway log("Failed to add the %s host key for IP address '%.30s' to the list of known hosts (%.30s).", 551e8aafc91SKris Kennaway type, ip, user_hostfile); 552e8aafc91SKris Kennaway else 553e8aafc91SKris Kennaway log("Warning: Permanently added the %s host key for IP address '%.30s' to the list of known hosts.", 554e8aafc91SKris Kennaway type, ip); 555e8aafc91SKris Kennaway } else if (ip_status != HOST_OK) 556e8aafc91SKris Kennaway log("Warning: the %s host key for '%.200s' differs from the key for the IP address '%.30s'", 557e8aafc91SKris Kennaway type, host, ip); 558e8aafc91SKris Kennaway } 559e8aafc91SKris Kennaway break; 560e8aafc91SKris Kennaway case HOST_NEW: 561e8aafc91SKris Kennaway /* The host is new. */ 562e8aafc91SKris Kennaway if (options.strict_host_key_checking == 1) { 563e8aafc91SKris Kennaway /* User has requested strict host key checking. We will not add the host key 564e8aafc91SKris Kennaway automatically. The only alternative left is to abort. */ 565e8aafc91SKris Kennaway fatal("No %s host key is known for %.200s and you have requested strict checking.", type, host); 566e8aafc91SKris Kennaway } else if (options.strict_host_key_checking == 2) { 567e8aafc91SKris Kennaway /* The default */ 568e8aafc91SKris Kennaway char prompt[1024]; 569e8aafc91SKris Kennaway char *fp = key_fingerprint(host_key); 570e8aafc91SKris Kennaway snprintf(prompt, sizeof(prompt), 571e8aafc91SKris Kennaway "The authenticity of host '%.200s' can't be established.\n" 572e8aafc91SKris Kennaway "%s key fingerprint is %s.\n" 573e8aafc91SKris Kennaway "Are you sure you want to continue connecting (yes/no)? ", 574e8aafc91SKris Kennaway host, type, fp); 575e8aafc91SKris Kennaway if (!read_yes_or_no(prompt, -1)) 576e8aafc91SKris Kennaway fatal("Aborted by user!\n"); 577e8aafc91SKris Kennaway } 578e8aafc91SKris Kennaway if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) { 579e8aafc91SKris Kennaway snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 580e8aafc91SKris Kennaway hostp = hostline; 581e8aafc91SKris Kennaway } else 582e8aafc91SKris Kennaway hostp = host; 583e8aafc91SKris Kennaway 584e8aafc91SKris Kennaway /* If not in strict mode, add the key automatically to the local known_hosts file. */ 585e8aafc91SKris Kennaway if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) 586e8aafc91SKris Kennaway log("Failed to add the host to the list of known hosts (%.500s).", 587e8aafc91SKris Kennaway user_hostfile); 588e8aafc91SKris Kennaway else 589e8aafc91SKris Kennaway log("Warning: Permanently added '%.200s' (%s) to the list of known hosts.", 590e8aafc91SKris Kennaway hostp, type); 591e8aafc91SKris Kennaway break; 592e8aafc91SKris Kennaway case HOST_CHANGED: 593e8aafc91SKris Kennaway if (options.check_host_ip && host_ip_differ) { 594e8aafc91SKris Kennaway char *msg; 595e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 596e8aafc91SKris Kennaway msg = "is unknown"; 597e8aafc91SKris Kennaway else if (ip_status == HOST_OK) 598e8aafc91SKris Kennaway msg = "is unchanged"; 599e8aafc91SKris Kennaway else 600e8aafc91SKris Kennaway msg = "has a different value"; 601e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 602e8aafc91SKris Kennaway error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 603e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 604e8aafc91SKris Kennaway error("The %s host key for %s has changed,", type, host); 605e8aafc91SKris Kennaway error("and the key for the according IP address %s", ip); 606e8aafc91SKris Kennaway error("%s. This could either mean that", msg); 607e8aafc91SKris Kennaway error("DNS SPOOFING is happening or the IP address for the host"); 608e8aafc91SKris Kennaway error("and its host key have changed at the same time"); 609e8aafc91SKris Kennaway } 610e8aafc91SKris Kennaway /* The host key has changed. */ 611e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 612e8aafc91SKris Kennaway error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 613e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 614e8aafc91SKris Kennaway error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 615e8aafc91SKris Kennaway error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 616e8aafc91SKris Kennaway error("It is also possible that the %s host key has just been changed.", type); 617e8aafc91SKris Kennaway error("Please contact your system administrator."); 618e8aafc91SKris Kennaway error("Add correct host key in %.100s to get rid of this message.", 619e8aafc91SKris Kennaway user_hostfile); 620e8aafc91SKris Kennaway 621e8aafc91SKris Kennaway /* 622e8aafc91SKris Kennaway * If strict host key checking is in use, the user will have 623e8aafc91SKris Kennaway * to edit the key manually and we can only abort. 624e8aafc91SKris Kennaway */ 625e8aafc91SKris Kennaway if (options.strict_host_key_checking) 626e8aafc91SKris Kennaway fatal("%s host key for %.200s has changed and you have requested strict checking.", type, host); 627e8aafc91SKris Kennaway 628e8aafc91SKris Kennaway /* 629e8aafc91SKris Kennaway * If strict host key checking has not been requested, allow 630e8aafc91SKris Kennaway * the connection but without password authentication or 631e8aafc91SKris Kennaway * agent forwarding. 632e8aafc91SKris Kennaway */ 633e8aafc91SKris Kennaway if (options.password_authentication) { 634e8aafc91SKris Kennaway error("Password authentication is disabled to avoid trojan horses."); 635e8aafc91SKris Kennaway options.password_authentication = 0; 636e8aafc91SKris Kennaway } 637e8aafc91SKris Kennaway if (options.forward_agent) { 638e8aafc91SKris Kennaway error("Agent forwarding is disabled to avoid trojan horses."); 639e8aafc91SKris Kennaway options.forward_agent = 0; 640e8aafc91SKris Kennaway } 641e8aafc91SKris Kennaway /* 642e8aafc91SKris Kennaway * XXX Should permit the user to change to use the new id. 643e8aafc91SKris Kennaway * This could be done by converting the host key to an 644e8aafc91SKris Kennaway * identifying sentence, tell that the host identifies itself 645e8aafc91SKris Kennaway * by that sentence, and ask the user if he/she whishes to 646e8aafc91SKris Kennaway * accept the authentication. 647e8aafc91SKris Kennaway */ 648e8aafc91SKris Kennaway break; 649e8aafc91SKris Kennaway } 650e8aafc91SKris Kennaway if (options.check_host_ip) 651e8aafc91SKris Kennaway xfree(ip); 652e8aafc91SKris Kennaway } 653511b41d2SMark Murray 654fe5fd017SMark Murray #ifdef KRB5 655fe5fd017SMark Murray int 656fe5fd017SMark Murray try_krb5_authentication(krb5_context *context, krb5_auth_context *auth_context) 657fe5fd017SMark Murray { 658fe5fd017SMark Murray krb5_error_code problem; 659fe5fd017SMark Murray const char *tkfile; 660fe5fd017SMark Murray struct stat buf; 661fe5fd017SMark Murray krb5_ccache ccache = NULL; 662fe5fd017SMark Murray const char *remotehost; 663fe5fd017SMark Murray krb5_data ap; 664fe5fd017SMark Murray int type, payload_len; 665fe5fd017SMark Murray krb5_ap_rep_enc_part *reply = NULL; 666fe5fd017SMark Murray int ret; 667fe5fd017SMark Murray 668fe5fd017SMark Murray memset(&ap, 0, sizeof(ap)); 669fe5fd017SMark Murray 670fe5fd017SMark Murray problem = krb5_init_context(context); 671fe5fd017SMark Murray if (problem) { 672fe5fd017SMark Murray ret = 0; 673fe5fd017SMark Murray goto out; 674fe5fd017SMark Murray } 675fe5fd017SMark Murray 676fe5fd017SMark Murray tkfile = krb5_cc_default_name(*context); 677fe5fd017SMark Murray if (strncmp(tkfile, "FILE:", 5) == 0) 678fe5fd017SMark Murray tkfile += 5; 679fe5fd017SMark Murray 680fe5fd017SMark Murray if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) { 681fe5fd017SMark Murray debug("Kerberos V5: could not get default ccache (permission denied)."); 682fe5fd017SMark Murray ret = 0; 683fe5fd017SMark Murray goto out; 684fe5fd017SMark Murray } 685fe5fd017SMark Murray 686fe5fd017SMark Murray problem = krb5_cc_default(*context, &ccache); 687fe5fd017SMark Murray if (problem) { 688fe5fd017SMark Murray ret = 0; 689fe5fd017SMark Murray goto out; 690fe5fd017SMark Murray } 691fe5fd017SMark Murray 692fe5fd017SMark Murray remotehost = get_canonical_hostname(); 693fe5fd017SMark Murray 694aeccfe99SAssar Westerlund problem = krb5_mk_req(*context, auth_context, AP_OPTS_MUTUAL_REQUIRED, 695aeccfe99SAssar Westerlund "host", remotehost, NULL, ccache, &ap); 696fe5fd017SMark Murray if (problem) { 697fe5fd017SMark Murray ret = 0; 698fe5fd017SMark Murray goto out; 699fe5fd017SMark Murray } 700fe5fd017SMark Murray 701cb96ab36SAssar Westerlund packet_start(SSH_CMSG_AUTH_KERBEROS); 702fe5fd017SMark Murray packet_put_string((char *) ap.data, ap.length); 703fe5fd017SMark Murray packet_send(); 704fe5fd017SMark Murray packet_write_wait(); 705fe5fd017SMark Murray 706fe5fd017SMark Murray xfree(ap.data); 707fe5fd017SMark Murray ap.length = 0; 708fe5fd017SMark Murray 709fe5fd017SMark Murray type = packet_read(&payload_len); 710fe5fd017SMark Murray switch (type) { 711fe5fd017SMark Murray case SSH_SMSG_FAILURE: 712cb96ab36SAssar Westerlund /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ 713fe5fd017SMark Murray debug("Kerberos V5 authentication failed."); 714fe5fd017SMark Murray ret = 0; 715fe5fd017SMark Murray break; 716fe5fd017SMark Murray 717cb96ab36SAssar Westerlund case SSH_SMSG_AUTH_KERBEROS_RESPONSE: 718cb96ab36SAssar Westerlund /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ 719fe5fd017SMark Murray debug("Kerberos V5 authentication accepted."); 720fe5fd017SMark Murray 721fe5fd017SMark Murray /* Get server's response. */ 722fe5fd017SMark Murray ap.data = packet_get_string((unsigned int *) &ap.length); 723fe5fd017SMark Murray 724fe5fd017SMark Murray packet_integrity_check(payload_len, 4 + ap.length, type); 725fe5fd017SMark Murray /* XXX je to dobre? */ 726fe5fd017SMark Murray 727fe5fd017SMark Murray problem = krb5_rd_rep(*context, *auth_context, &ap, &reply); 728fe5fd017SMark Murray if (problem) { 729fe5fd017SMark Murray ret = 0; 730fe5fd017SMark Murray } 731fe5fd017SMark Murray ret = 1; 732fe5fd017SMark Murray break; 733fe5fd017SMark Murray 734fe5fd017SMark Murray default: 735fe5fd017SMark Murray packet_disconnect("Protocol error on Kerberos V5 response: %d", type); 736fe5fd017SMark Murray ret = 0; 737fe5fd017SMark Murray break; 738fe5fd017SMark Murray 739fe5fd017SMark Murray } 740fe5fd017SMark Murray 741fe5fd017SMark Murray out: 742fe5fd017SMark Murray if (ccache != NULL) 743fe5fd017SMark Murray krb5_cc_close(*context, ccache); 744fe5fd017SMark Murray if (reply != NULL) 745fe5fd017SMark Murray krb5_free_ap_rep_enc_part(*context, reply); 746fe5fd017SMark Murray if (ap.length > 0) 747fe5fd017SMark Murray krb5_data_free(&ap); 748fe5fd017SMark Murray 749fe5fd017SMark Murray return ret; 750fe5fd017SMark Murray 751fe5fd017SMark Murray } 752fe5fd017SMark Murray 753fe5fd017SMark Murray void 754fe5fd017SMark Murray send_krb5_tgt(krb5_context context, krb5_auth_context auth_context) 755fe5fd017SMark Murray { 756fe5fd017SMark Murray int fd; 757fe5fd017SMark Murray int type, payload_len; 758fe5fd017SMark Murray krb5_error_code problem; 759fe5fd017SMark Murray krb5_data outbuf; 760fe5fd017SMark Murray krb5_ccache ccache = NULL; 761fe5fd017SMark Murray krb5_creds creds; 762fe5fd017SMark Murray krb5_kdc_flags flags; 763fe5fd017SMark Murray const char* remotehost = get_canonical_hostname(); 764fe5fd017SMark Murray 765fe5fd017SMark Murray memset(&creds, 0, sizeof(creds)); 766fe5fd017SMark Murray memset(&outbuf, 0, sizeof(outbuf)); 767fe5fd017SMark Murray 768fe5fd017SMark Murray fd = packet_get_connection_in(); 769fe5fd017SMark Murray problem = krb5_auth_con_setaddrs_from_fd(context, auth_context, &fd); 770fe5fd017SMark Murray if (problem) { 771fe5fd017SMark Murray goto out; 772fe5fd017SMark Murray } 773fe5fd017SMark Murray 774fe5fd017SMark Murray #if 0 775fe5fd017SMark Murray tkfile = krb5_cc_default_name(context); 776fe5fd017SMark Murray if (strncmp(tkfile, "FILE:", 5) == 0) 777fe5fd017SMark Murray tkfile += 5; 778fe5fd017SMark Murray 779fe5fd017SMark Murray if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) { 780fe5fd017SMark Murray debug("Kerberos V5: could not get default ccache (permission denied)."); 781fe5fd017SMark Murray goto out; 782fe5fd017SMark Murray } 783fe5fd017SMark Murray #endif 784fe5fd017SMark Murray 785fe5fd017SMark Murray problem = krb5_cc_default(context, &ccache); 786fe5fd017SMark Murray if (problem) { 787fe5fd017SMark Murray goto out; 788fe5fd017SMark Murray } 789fe5fd017SMark Murray 790fe5fd017SMark Murray problem = krb5_cc_get_principal(context, ccache, &creds.client); 791fe5fd017SMark Murray if (problem) { 792fe5fd017SMark Murray goto out; 793fe5fd017SMark Murray } 794fe5fd017SMark Murray 795fe5fd017SMark Murray problem = krb5_build_principal(context, &creds.server, 796fe5fd017SMark Murray strlen(creds.client->realm), 797fe5fd017SMark Murray creds.client->realm, 798fe5fd017SMark Murray "krbtgt", 799fe5fd017SMark Murray creds.client->realm, 800fe5fd017SMark Murray NULL); 801fe5fd017SMark Murray if (problem) { 802fe5fd017SMark Murray goto out; 803fe5fd017SMark Murray } 804fe5fd017SMark Murray 805fe5fd017SMark Murray creds.times.endtime = 0; 806fe5fd017SMark Murray 807fe5fd017SMark Murray flags.i = 0; 808fe5fd017SMark Murray flags.b.forwarded = 1; 809fe5fd017SMark Murray flags.b.forwardable = krb5_config_get_bool(context, NULL, 810fe5fd017SMark Murray "libdefaults", "forwardable", NULL); 811fe5fd017SMark Murray 812fe5fd017SMark Murray problem = krb5_get_forwarded_creds (context, 813fe5fd017SMark Murray auth_context, 814fe5fd017SMark Murray ccache, 815fe5fd017SMark Murray flags.i, 816fe5fd017SMark Murray remotehost, 817fe5fd017SMark Murray &creds, 818fe5fd017SMark Murray &outbuf); 819fe5fd017SMark Murray if (problem) { 820fe5fd017SMark Murray goto out; 821fe5fd017SMark Murray } 822fe5fd017SMark Murray 823cb96ab36SAssar Westerlund packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); 824fe5fd017SMark Murray packet_put_string((char *)outbuf.data, outbuf.length); 825fe5fd017SMark Murray packet_send(); 826fe5fd017SMark Murray packet_write_wait(); 827fe5fd017SMark Murray 828fe5fd017SMark Murray type = packet_read(&payload_len); 829fe5fd017SMark Murray switch (type) { 830fe5fd017SMark Murray case SSH_SMSG_SUCCESS: 831fe5fd017SMark Murray break; 832fe5fd017SMark Murray case SSH_SMSG_FAILURE: 833fe5fd017SMark Murray break; 834fe5fd017SMark Murray default: 835fe5fd017SMark Murray break; 836fe5fd017SMark Murray } 837fe5fd017SMark Murray 838fe5fd017SMark Murray out: 839fe5fd017SMark Murray if (creds.client) 840fe5fd017SMark Murray krb5_free_principal(context, creds.client); 841fe5fd017SMark Murray if (creds.server) 842fe5fd017SMark Murray krb5_free_principal(context, creds.server); 843fe5fd017SMark Murray if (ccache) 844fe5fd017SMark Murray krb5_cc_close(context, ccache); 845fe5fd017SMark Murray if (outbuf.data) 846fe5fd017SMark Murray xfree(outbuf.data); 847fe5fd017SMark Murray 848fe5fd017SMark Murray return; 849fe5fd017SMark Murray } 850fe5fd017SMark Murray #endif /* KRB5 */ 851fe5fd017SMark Murray 852511b41d2SMark Murray /* 853511b41d2SMark Murray * Starts a dialog with the server, and authenticates the current user on the 854511b41d2SMark Murray * server. This does not need any extra privileges. The basic connection 855511b41d2SMark Murray * to the server must already have been established before this is called. 856511b41d2SMark Murray * If login fails, this function prints an error and never returns. 857511b41d2SMark Murray * This function does not require super-user privileges. 858511b41d2SMark Murray */ 859511b41d2SMark Murray void 860511b41d2SMark Murray ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, 861511b41d2SMark Murray struct sockaddr *hostaddr, uid_t original_real_uid) 862511b41d2SMark Murray { 863e8aafc91SKris Kennaway struct passwd *pw; 864511b41d2SMark Murray char *host, *cp; 865e8aafc91SKris Kennaway char *server_user, *local_user; 866e8aafc91SKris Kennaway 867e8aafc91SKris Kennaway /* Get local user name. Use it as server user if no user name was given. */ 868e8aafc91SKris Kennaway pw = getpwuid(original_real_uid); 869e8aafc91SKris Kennaway if (!pw) 870c2d3a559SKris Kennaway fatal("User id %u not found from user database.", original_real_uid); 871e8aafc91SKris Kennaway local_user = xstrdup(pw->pw_name); 872e8aafc91SKris Kennaway server_user = options.user ? options.user : local_user; 873511b41d2SMark Murray 874511b41d2SMark Murray /* Convert the user-supplied hostname into all lowercase. */ 875511b41d2SMark Murray host = xstrdup(orighost); 876511b41d2SMark Murray for (cp = host; *cp; cp++) 877511b41d2SMark Murray if (isupper(*cp)) 878511b41d2SMark Murray *cp = tolower(*cp); 879511b41d2SMark Murray 880511b41d2SMark Murray /* Exchange protocol version identification strings with the server. */ 881511b41d2SMark Murray ssh_exchange_identification(); 882511b41d2SMark Murray 883511b41d2SMark Murray /* Put the connection into non-blocking mode. */ 884511b41d2SMark Murray packet_set_nonblocking(); 885511b41d2SMark Murray 886511b41d2SMark Murray /* key exchange */ 887511b41d2SMark Murray /* authenticate user */ 888e8aafc91SKris Kennaway if (compat20) { 889e8aafc91SKris Kennaway ssh_kex2(host, hostaddr); 890e8aafc91SKris Kennaway ssh_userauth2(server_user, host); 891e8aafc91SKris Kennaway } else { 892e8aafc91SKris Kennaway ssh_kex(host, hostaddr); 893e8aafc91SKris Kennaway ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key); 894e8aafc91SKris Kennaway } 895511b41d2SMark Murray } 896e0fbb1d2SBrian Feldman 897e0fbb1d2SBrian Feldman void 898e0fbb1d2SBrian Feldman ssh_put_password(char *password) 899e0fbb1d2SBrian Feldman { 900e0fbb1d2SBrian Feldman int size; 901e0fbb1d2SBrian Feldman char *padded; 902e0fbb1d2SBrian Feldman 903e0fbb1d2SBrian Feldman size = roundup(strlen(password) + 1, 32); 904e0fbb1d2SBrian Feldman padded = xmalloc(size); 905e0fbb1d2SBrian Feldman memset(padded, 0, size); 906e0fbb1d2SBrian Feldman strlcpy(padded, password, size); 907e0fbb1d2SBrian Feldman packet_put_string(padded, size); 908e0fbb1d2SBrian Feldman memset(padded, 0, size); 909e0fbb1d2SBrian Feldman xfree(padded); 910e0fbb1d2SBrian Feldman } 911