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. 17609958426SBrian Feldman * The canonical host name used to connect will be returned in *host. 177511b41d2SMark Murray * The address of the remote host will be returned in hostaddr. 178511b41d2SMark Murray * If port is 0, the default port will be used. If anonymous is zero, 179511b41d2SMark Murray * a privileged port will be allocated to make the connection. 180511b41d2SMark Murray * This requires super-user privileges if anonymous is false. 181511b41d2SMark Murray * Connection_attempts specifies the maximum number of tries (one per 182511b41d2SMark Murray * second). If proxy_command is non-NULL, it specifies the command (with %h 183511b41d2SMark Murray * and %p substituted for host and port, respectively) to use to contact 184511b41d2SMark Murray * the daemon. 185511b41d2SMark Murray */ 186511b41d2SMark Murray int 18709958426SBrian Feldman ssh_connect(char **host, struct sockaddr_storage * hostaddr, 188511b41d2SMark Murray u_short port, int connection_attempts, 189511b41d2SMark Murray int anonymous, uid_t original_real_uid, 190511b41d2SMark Murray const char *proxy_command) 191511b41d2SMark Murray { 192511b41d2SMark Murray int sock = -1, attempt; 193511b41d2SMark Murray struct servent *sp; 194511b41d2SMark Murray struct addrinfo hints, *ai, *aitop; 195511b41d2SMark Murray char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 196511b41d2SMark Murray int gaierr; 197511b41d2SMark Murray struct linger linger; 198511b41d2SMark Murray 199c2d3a559SKris Kennaway debug("ssh_connect: getuid %u geteuid %u anon %d", 200c2d3a559SKris Kennaway (u_int) getuid(), (u_int) geteuid(), anonymous); 201511b41d2SMark Murray 202511b41d2SMark Murray /* Get default port if port has not been set. */ 203511b41d2SMark Murray if (port == 0) { 204511b41d2SMark Murray sp = getservbyname(SSH_SERVICE_NAME, "tcp"); 205511b41d2SMark Murray if (sp) 206511b41d2SMark Murray port = ntohs(sp->s_port); 207511b41d2SMark Murray else 208511b41d2SMark Murray port = SSH_DEFAULT_PORT; 209511b41d2SMark Murray } 210511b41d2SMark Murray /* If a proxy command is given, connect using it. */ 211511b41d2SMark Murray if (proxy_command != NULL) 21209958426SBrian Feldman return ssh_proxy_connect(*host, port, original_real_uid, proxy_command); 213511b41d2SMark Murray 214511b41d2SMark Murray /* No proxy command. */ 215511b41d2SMark Murray 216511b41d2SMark Murray memset(&hints, 0, sizeof(hints)); 217511b41d2SMark Murray hints.ai_family = IPv4or6; 218511b41d2SMark Murray hints.ai_socktype = SOCK_STREAM; 21909958426SBrian Feldman hints.ai_flags = AI_CANONNAME; 220511b41d2SMark Murray snprintf(strport, sizeof strport, "%d", port); 22109958426SBrian Feldman if ((gaierr = getaddrinfo(*host, strport, &hints, &aitop)) != 0) 22209958426SBrian Feldman fatal("%s: %.100s: %s", __progname, *host, 223511b41d2SMark Murray gai_strerror(gaierr)); 224511b41d2SMark Murray 225511b41d2SMark Murray /* 226511b41d2SMark Murray * Try to connect several times. On some machines, the first time 227511b41d2SMark Murray * will sometimes fail. In general socket code appears to behave 228511b41d2SMark Murray * quite magically on many machines. 229511b41d2SMark Murray */ 230511b41d2SMark Murray for (attempt = 0; attempt < connection_attempts; attempt++) { 231511b41d2SMark Murray if (attempt > 0) 232511b41d2SMark Murray debug("Trying again..."); 233511b41d2SMark Murray 234511b41d2SMark Murray /* Loop through addresses for this host, and try each one in 235511b41d2SMark Murray sequence until the connection succeeds. */ 236511b41d2SMark Murray for (ai = aitop; ai; ai = ai->ai_next) { 237511b41d2SMark Murray if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 238511b41d2SMark Murray continue; 239511b41d2SMark Murray if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 240511b41d2SMark Murray ntop, sizeof(ntop), strport, sizeof(strport), 241511b41d2SMark Murray NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 242511b41d2SMark Murray error("ssh_connect: getnameinfo failed"); 243511b41d2SMark Murray continue; 244511b41d2SMark Murray } 245511b41d2SMark Murray debug("Connecting to %.200s [%.100s] port %s.", 24609958426SBrian Feldman ai->ai_canonname, ntop, strport); 247511b41d2SMark Murray 248511b41d2SMark Murray /* Create a socket for connecting. */ 249511b41d2SMark Murray sock = ssh_create_socket(original_real_uid, 250511b41d2SMark Murray !anonymous && geteuid() == 0 && port < IPPORT_RESERVED, 251511b41d2SMark Murray ai->ai_family); 252511b41d2SMark Murray if (sock < 0) 253511b41d2SMark Murray continue; 254511b41d2SMark Murray 255511b41d2SMark Murray /* Connect to the host. We use the user's uid in the 256511b41d2SMark Murray * hope that it will help with tcp_wrappers showing 257511b41d2SMark Murray * the remote uid as root. 258511b41d2SMark Murray */ 259511b41d2SMark Murray temporarily_use_uid(original_real_uid); 260511b41d2SMark Murray if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { 261511b41d2SMark Murray /* Successful connection. */ 262c322fe35SKris Kennaway memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 263511b41d2SMark Murray restore_uid(); 264511b41d2SMark Murray break; 265511b41d2SMark Murray } else { 266511b41d2SMark Murray debug("connect: %.100s", strerror(errno)); 267511b41d2SMark Murray restore_uid(); 268511b41d2SMark Murray /* 269511b41d2SMark Murray * Close the failed socket; there appear to 270511b41d2SMark Murray * be some problems when reusing a socket for 271511b41d2SMark Murray * which connect() has already returned an 272511b41d2SMark Murray * error. 273511b41d2SMark Murray */ 274511b41d2SMark Murray shutdown(sock, SHUT_RDWR); 275511b41d2SMark Murray close(sock); 276511b41d2SMark Murray } 277511b41d2SMark Murray } 27809958426SBrian Feldman if (ai) { 279a61d605eSBrian Feldman #if 0 28009958426SBrian Feldman if (ai->ai_canonname != NULL) 28109958426SBrian Feldman *host = xstrdup(ai->ai_canonname); 282a61d605eSBrian Feldman #endif 283511b41d2SMark Murray break; /* Successful connection. */ 28409958426SBrian Feldman } 285511b41d2SMark Murray 286511b41d2SMark Murray /* Sleep a moment before retrying. */ 287511b41d2SMark Murray sleep(1); 288511b41d2SMark Murray } 289511b41d2SMark Murray 290511b41d2SMark Murray freeaddrinfo(aitop); 291511b41d2SMark Murray 292511b41d2SMark Murray /* Return failure if we didn't get a successful connection. */ 293511b41d2SMark Murray if (attempt >= connection_attempts) 294511b41d2SMark Murray return 0; 295511b41d2SMark Murray 296511b41d2SMark Murray debug("Connection established."); 297511b41d2SMark Murray 298511b41d2SMark Murray /* 299511b41d2SMark Murray * Set socket options. We would like the socket to disappear as soon 300511b41d2SMark Murray * as it has been closed for whatever reason. 301511b41d2SMark Murray */ 302511b41d2SMark Murray /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ 303511b41d2SMark Murray linger.l_onoff = 1; 304511b41d2SMark Murray linger.l_linger = 5; 305511b41d2SMark Murray setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); 306511b41d2SMark Murray 307511b41d2SMark Murray /* Set the connection. */ 308511b41d2SMark Murray packet_set_connection(sock, sock); 309511b41d2SMark Murray 310511b41d2SMark Murray return 1; 311511b41d2SMark Murray } 312511b41d2SMark Murray 313511b41d2SMark Murray /* 314e8aafc91SKris Kennaway * Waits for the server identification string, and sends our own 315e8aafc91SKris Kennaway * identification string. 316511b41d2SMark Murray */ 317511b41d2SMark Murray void 318e8aafc91SKris Kennaway ssh_exchange_identification() 319511b41d2SMark Murray { 320e8aafc91SKris Kennaway char buf[256], remote_version[256]; /* must be same size! */ 321e8aafc91SKris Kennaway int remote_major, remote_minor, i, mismatch; 322e8aafc91SKris Kennaway int connection_in = packet_get_connection_in(); 323e8aafc91SKris Kennaway int connection_out = packet_get_connection_out(); 324511b41d2SMark Murray 325e8aafc91SKris Kennaway /* Read other side\'s version identification. */ 326c2d3a559SKris Kennaway for (;;) { 327e8aafc91SKris Kennaway for (i = 0; i < sizeof(buf) - 1; i++) { 328c2d3a559SKris Kennaway int len = atomicio(read, connection_in, &buf[i], 1); 329e8aafc91SKris Kennaway if (len < 0) 330e8aafc91SKris Kennaway fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); 331e8aafc91SKris Kennaway if (len != 1) 332e8aafc91SKris Kennaway fatal("ssh_exchange_identification: Connection closed by remote host"); 333e8aafc91SKris Kennaway if (buf[i] == '\r') { 334e8aafc91SKris Kennaway buf[i] = '\n'; 335e8aafc91SKris Kennaway buf[i + 1] = 0; 336e8aafc91SKris Kennaway continue; /**XXX wait for \n */ 337511b41d2SMark Murray } 338e8aafc91SKris Kennaway if (buf[i] == '\n') { 339e8aafc91SKris Kennaway buf[i + 1] = 0; 340511b41d2SMark Murray break; 341e8aafc91SKris Kennaway } 342e8aafc91SKris Kennaway } 343e8aafc91SKris Kennaway buf[sizeof(buf) - 1] = 0; 344c2d3a559SKris Kennaway if (strncmp(buf, "SSH-", 4) == 0) 345c2d3a559SKris Kennaway break; 346c2d3a559SKris Kennaway debug("ssh_exchange_identification: %s", buf); 347c2d3a559SKris Kennaway } 348e8aafc91SKris Kennaway server_version_string = xstrdup(buf); 349511b41d2SMark Murray 350511b41d2SMark Murray /* 351e8aafc91SKris Kennaway * Check that the versions match. In future this might accept 352e8aafc91SKris Kennaway * several versions and set appropriate flags to handle them. 353511b41d2SMark Murray */ 354e8aafc91SKris Kennaway if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 355e8aafc91SKris Kennaway &remote_major, &remote_minor, remote_version) != 3) 356e8aafc91SKris Kennaway fatal("Bad remote protocol version identification: '%.100s'", buf); 357e8aafc91SKris Kennaway debug("Remote protocol version %d.%d, remote software version %.100s", 358e8aafc91SKris Kennaway remote_major, remote_minor, remote_version); 359511b41d2SMark Murray 360e8aafc91SKris Kennaway compat_datafellows(remote_version); 361e8aafc91SKris Kennaway mismatch = 0; 362e8aafc91SKris Kennaway 363e8aafc91SKris Kennaway switch(remote_major) { 364e8aafc91SKris Kennaway case 1: 365e8aafc91SKris Kennaway if (remote_minor == 99 && 366e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) && 367e8aafc91SKris Kennaway !(options.protocol & SSH_PROTO_1_PREFERRED)) { 368e8aafc91SKris Kennaway enable_compat20(); 369511b41d2SMark Murray break; 370e8aafc91SKris Kennaway } 371e8aafc91SKris Kennaway if (!(options.protocol & SSH_PROTO_1)) { 372e8aafc91SKris Kennaway mismatch = 1; 373e8aafc91SKris Kennaway break; 374e8aafc91SKris Kennaway } 375e8aafc91SKris Kennaway if (remote_minor < 3) { 376e8aafc91SKris Kennaway fatal("Remote machine has too old SSH software version."); 377e8aafc91SKris Kennaway } else if (remote_minor == 3) { 378e8aafc91SKris Kennaway /* We speak 1.3, too. */ 379e8aafc91SKris Kennaway enable_compat13(); 380e8aafc91SKris Kennaway if (options.forward_agent) { 381e8aafc91SKris Kennaway log("Agent forwarding disabled for protocol 1.3"); 382e8aafc91SKris Kennaway options.forward_agent = 0; 383e8aafc91SKris Kennaway } 384e8aafc91SKris Kennaway } 385e8aafc91SKris Kennaway break; 386e8aafc91SKris Kennaway case 2: 387e8aafc91SKris Kennaway if (options.protocol & SSH_PROTO_2) { 388e8aafc91SKris Kennaway enable_compat20(); 389e8aafc91SKris Kennaway break; 390e8aafc91SKris Kennaway } 391e8aafc91SKris Kennaway /* FALLTHROUGH */ 392511b41d2SMark Murray default: 393e8aafc91SKris Kennaway mismatch = 1; 394e8aafc91SKris Kennaway break; 395511b41d2SMark Murray } 396e8aafc91SKris Kennaway if (mismatch) 397e8aafc91SKris Kennaway fatal("Protocol major versions differ: %d vs. %d", 398e8aafc91SKris Kennaway (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 399e8aafc91SKris Kennaway remote_major); 400e8aafc91SKris Kennaway if (compat20) 401e8aafc91SKris Kennaway packet_set_ssh2_format(); 402e8aafc91SKris Kennaway /* Send our own protocol version identification. */ 403e8aafc91SKris Kennaway snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 404e8aafc91SKris Kennaway compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 405e8aafc91SKris Kennaway compat20 ? PROTOCOL_MINOR_2 : PROTOCOL_MINOR_1, 406e8aafc91SKris Kennaway SSH_VERSION); 407e8aafc91SKris Kennaway if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) 408e8aafc91SKris Kennaway fatal("write: %.100s", strerror(errno)); 409e8aafc91SKris Kennaway client_version_string = xstrdup(buf); 410e8aafc91SKris Kennaway chop(client_version_string); 411e8aafc91SKris Kennaway chop(server_version_string); 412e8aafc91SKris Kennaway debug("Local version string %.100s", client_version_string); 413511b41d2SMark Murray } 414511b41d2SMark Murray 415511b41d2SMark Murray int 416e8aafc91SKris Kennaway read_yes_or_no(const char *prompt, int defval) 417511b41d2SMark Murray { 418e8aafc91SKris Kennaway char buf[1024]; 419e8aafc91SKris Kennaway FILE *f; 420e8aafc91SKris Kennaway int retval = -1; 421511b41d2SMark Murray 422e8aafc91SKris Kennaway if (isatty(0)) 423e8aafc91SKris Kennaway f = stdin; 424e8aafc91SKris Kennaway else 425e8aafc91SKris Kennaway f = fopen("/dev/tty", "rw"); 426e8aafc91SKris Kennaway 427e8aafc91SKris Kennaway if (f == NULL) 428511b41d2SMark Murray return 0; 429511b41d2SMark Murray 430e8aafc91SKris Kennaway fflush(stdout); 431511b41d2SMark Murray 432e8aafc91SKris Kennaway while (1) { 433e8aafc91SKris Kennaway fprintf(stderr, "%s", prompt); 434e8aafc91SKris Kennaway if (fgets(buf, sizeof(buf), f) == NULL) { 435e8aafc91SKris Kennaway /* Print a newline (the prompt probably didn\'t have one). */ 436e8aafc91SKris Kennaway fprintf(stderr, "\n"); 437e8aafc91SKris Kennaway strlcpy(buf, "no", sizeof buf); 438511b41d2SMark Murray } 439e8aafc91SKris Kennaway /* Remove newline from response. */ 440e8aafc91SKris Kennaway if (strchr(buf, '\n')) 441e8aafc91SKris Kennaway *strchr(buf, '\n') = 0; 442e8aafc91SKris Kennaway 443e8aafc91SKris Kennaway if (buf[0] == 0) 444e8aafc91SKris Kennaway retval = defval; 445e8aafc91SKris Kennaway if (strcmp(buf, "yes") == 0) 446e8aafc91SKris Kennaway retval = 1; 44709958426SBrian Feldman else if (strcmp(buf, "no") == 0) 448e8aafc91SKris Kennaway retval = 0; 44909958426SBrian Feldman else 45009958426SBrian Feldman fprintf(stderr, "Please type 'yes' or 'no'.\n"); 451e8aafc91SKris Kennaway 452e8aafc91SKris Kennaway if (retval != -1) { 453e8aafc91SKris Kennaway if (f != stdin) 454e8aafc91SKris Kennaway fclose(f); 455e8aafc91SKris Kennaway return retval; 456511b41d2SMark Murray } 457511b41d2SMark Murray } 458511b41d2SMark Murray } 459511b41d2SMark Murray 460e8aafc91SKris Kennaway /* 461e8aafc91SKris Kennaway * check whether the supplied host key is valid, return only if ok. 462e8aafc91SKris Kennaway */ 463e8aafc91SKris Kennaway 464511b41d2SMark Murray void 465e8aafc91SKris Kennaway check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, 466e8aafc91SKris Kennaway const char *user_hostfile, const char *system_hostfile) 467511b41d2SMark Murray { 468e8aafc91SKris Kennaway Key *file_key; 469e8aafc91SKris Kennaway char *type = key_type(host_key); 470e8aafc91SKris Kennaway char *ip = NULL; 471e8aafc91SKris Kennaway char hostline[1000], *hostp; 472e8aafc91SKris Kennaway HostStatus host_status; 473e8aafc91SKris Kennaway HostStatus ip_status; 474e8aafc91SKris Kennaway int local = 0, host_ip_differ = 0; 475e8aafc91SKris Kennaway char ntop[NI_MAXHOST]; 476511b41d2SMark Murray 477e8aafc91SKris Kennaway /* 478e8aafc91SKris Kennaway * Force accepting of the host key for loopback/localhost. The 479e8aafc91SKris Kennaway * problem is that if the home directory is NFS-mounted to multiple 480e8aafc91SKris Kennaway * machines, localhost will refer to a different machine in each of 481e8aafc91SKris Kennaway * them, and the user will get bogus HOST_CHANGED warnings. This 482e8aafc91SKris Kennaway * essentially disables host authentication for localhost; however, 483e8aafc91SKris Kennaway * this is probably not a real problem. 484e8aafc91SKris Kennaway */ 485e8aafc91SKris Kennaway /** hostaddr == 0! */ 486e8aafc91SKris Kennaway switch (hostaddr->sa_family) { 487e8aafc91SKris Kennaway case AF_INET: 488e8aafc91SKris Kennaway local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 489511b41d2SMark Murray break; 490e8aafc91SKris Kennaway case AF_INET6: 491e8aafc91SKris Kennaway local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 492511b41d2SMark Murray break; 493e8aafc91SKris Kennaway default: 494e8aafc91SKris Kennaway local = 0; 495511b41d2SMark Murray break; 496511b41d2SMark Murray } 497e8aafc91SKris Kennaway if (local) { 498e8aafc91SKris Kennaway debug("Forcing accepting of host key for loopback/localhost."); 499e8aafc91SKris Kennaway return; 500511b41d2SMark Murray } 501511b41d2SMark Murray 502e8aafc91SKris Kennaway /* 503e8aafc91SKris Kennaway * Turn off check_host_ip for proxy connects, since 504e8aafc91SKris Kennaway * we don't have the remote ip-address 505e8aafc91SKris Kennaway */ 506e8aafc91SKris Kennaway if (options.proxy_command != NULL && options.check_host_ip) 507e8aafc91SKris Kennaway options.check_host_ip = 0; 508e8aafc91SKris Kennaway 509e8aafc91SKris Kennaway if (options.check_host_ip) { 510e8aafc91SKris Kennaway if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop), 511e8aafc91SKris Kennaway NULL, 0, NI_NUMERICHOST) != 0) 512e8aafc91SKris Kennaway fatal("check_host_key: getnameinfo failed"); 513e8aafc91SKris Kennaway ip = xstrdup(ntop); 514e8aafc91SKris Kennaway } 515e8aafc91SKris Kennaway 516e8aafc91SKris Kennaway /* 517e8aafc91SKris Kennaway * Store the host key from the known host file in here so that we can 518e8aafc91SKris Kennaway * compare it with the key for the IP address. 519e8aafc91SKris Kennaway */ 520e8aafc91SKris Kennaway file_key = key_new(host_key->type); 521e8aafc91SKris Kennaway 522e8aafc91SKris Kennaway /* 523e8aafc91SKris Kennaway * Check if the host key is present in the user\'s list of known 524e8aafc91SKris Kennaway * hosts or in the systemwide list. 525e8aafc91SKris Kennaway */ 526e8aafc91SKris Kennaway host_status = check_host_in_hostfile(user_hostfile, host, host_key, file_key); 527e8aafc91SKris Kennaway if (host_status == HOST_NEW) 528e8aafc91SKris Kennaway host_status = check_host_in_hostfile(system_hostfile, host, host_key, file_key); 529e8aafc91SKris Kennaway /* 530e8aafc91SKris Kennaway * Also perform check for the ip address, skip the check if we are 531e8aafc91SKris Kennaway * localhost or the hostname was an ip address to begin with 532e8aafc91SKris Kennaway */ 533e8aafc91SKris Kennaway if (options.check_host_ip && !local && strcmp(host, ip)) { 534e8aafc91SKris Kennaway Key *ip_key = key_new(host_key->type); 535e8aafc91SKris Kennaway ip_status = check_host_in_hostfile(user_hostfile, ip, host_key, ip_key); 536e8aafc91SKris Kennaway 537e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 538e8aafc91SKris Kennaway ip_status = check_host_in_hostfile(system_hostfile, ip, host_key, ip_key); 539e8aafc91SKris Kennaway if (host_status == HOST_CHANGED && 540e8aafc91SKris Kennaway (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) 541e8aafc91SKris Kennaway host_ip_differ = 1; 542e8aafc91SKris Kennaway 543e8aafc91SKris Kennaway key_free(ip_key); 544e8aafc91SKris Kennaway } else 545e8aafc91SKris Kennaway ip_status = host_status; 546e8aafc91SKris Kennaway 547e8aafc91SKris Kennaway key_free(file_key); 548e8aafc91SKris Kennaway 549e8aafc91SKris Kennaway switch (host_status) { 550e8aafc91SKris Kennaway case HOST_OK: 551e8aafc91SKris Kennaway /* The host is known and the key matches. */ 552e8aafc91SKris Kennaway debug("Host '%.200s' is known and matches the %s host key.", 553e8aafc91SKris Kennaway host, type); 554e8aafc91SKris Kennaway if (options.check_host_ip) { 555e8aafc91SKris Kennaway if (ip_status == HOST_NEW) { 556e8aafc91SKris Kennaway if (!add_host_to_hostfile(user_hostfile, ip, host_key)) 557e8aafc91SKris Kennaway log("Failed to add the %s host key for IP address '%.30s' to the list of known hosts (%.30s).", 558e8aafc91SKris Kennaway type, ip, user_hostfile); 559e8aafc91SKris Kennaway else 560e8aafc91SKris Kennaway log("Warning: Permanently added the %s host key for IP address '%.30s' to the list of known hosts.", 561e8aafc91SKris Kennaway type, ip); 562e8aafc91SKris Kennaway } else if (ip_status != HOST_OK) 563e8aafc91SKris Kennaway log("Warning: the %s host key for '%.200s' differs from the key for the IP address '%.30s'", 564e8aafc91SKris Kennaway type, host, ip); 565e8aafc91SKris Kennaway } 566e8aafc91SKris Kennaway break; 567e8aafc91SKris Kennaway case HOST_NEW: 568e8aafc91SKris Kennaway /* The host is new. */ 569e8aafc91SKris Kennaway if (options.strict_host_key_checking == 1) { 570e8aafc91SKris Kennaway /* User has requested strict host key checking. We will not add the host key 571e8aafc91SKris Kennaway automatically. The only alternative left is to abort. */ 572e8aafc91SKris Kennaway fatal("No %s host key is known for %.200s and you have requested strict checking.", type, host); 573e8aafc91SKris Kennaway } else if (options.strict_host_key_checking == 2) { 574e8aafc91SKris Kennaway /* The default */ 575e8aafc91SKris Kennaway char prompt[1024]; 576e8aafc91SKris Kennaway char *fp = key_fingerprint(host_key); 577e8aafc91SKris Kennaway snprintf(prompt, sizeof(prompt), 578e8aafc91SKris Kennaway "The authenticity of host '%.200s' can't be established.\n" 579e8aafc91SKris Kennaway "%s key fingerprint is %s.\n" 580e8aafc91SKris Kennaway "Are you sure you want to continue connecting (yes/no)? ", 581e8aafc91SKris Kennaway host, type, fp); 582e8aafc91SKris Kennaway if (!read_yes_or_no(prompt, -1)) 583e8aafc91SKris Kennaway fatal("Aborted by user!\n"); 584e8aafc91SKris Kennaway } 585e8aafc91SKris Kennaway if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) { 586e8aafc91SKris Kennaway snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 587e8aafc91SKris Kennaway hostp = hostline; 588e8aafc91SKris Kennaway } else 589e8aafc91SKris Kennaway hostp = host; 590e8aafc91SKris Kennaway 591e8aafc91SKris Kennaway /* If not in strict mode, add the key automatically to the local known_hosts file. */ 592e8aafc91SKris Kennaway if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) 593e8aafc91SKris Kennaway log("Failed to add the host to the list of known hosts (%.500s).", 594e8aafc91SKris Kennaway user_hostfile); 595e8aafc91SKris Kennaway else 596e8aafc91SKris Kennaway log("Warning: Permanently added '%.200s' (%s) to the list of known hosts.", 597e8aafc91SKris Kennaway hostp, type); 598e8aafc91SKris Kennaway break; 599e8aafc91SKris Kennaway case HOST_CHANGED: 600e8aafc91SKris Kennaway if (options.check_host_ip && host_ip_differ) { 601e8aafc91SKris Kennaway char *msg; 602e8aafc91SKris Kennaway if (ip_status == HOST_NEW) 603e8aafc91SKris Kennaway msg = "is unknown"; 604e8aafc91SKris Kennaway else if (ip_status == HOST_OK) 605e8aafc91SKris Kennaway msg = "is unchanged"; 606e8aafc91SKris Kennaway else 607e8aafc91SKris Kennaway msg = "has a different value"; 608e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 609e8aafc91SKris Kennaway error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 610e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 611e8aafc91SKris Kennaway error("The %s host key for %s has changed,", type, host); 612e8aafc91SKris Kennaway error("and the key for the according IP address %s", ip); 613e8aafc91SKris Kennaway error("%s. This could either mean that", msg); 614e8aafc91SKris Kennaway error("DNS SPOOFING is happening or the IP address for the host"); 615e8aafc91SKris Kennaway error("and its host key have changed at the same time"); 616e8aafc91SKris Kennaway } 617e8aafc91SKris Kennaway /* The host key has changed. */ 618e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 619e8aafc91SKris Kennaway error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 620e8aafc91SKris Kennaway error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 621e8aafc91SKris Kennaway error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 622e8aafc91SKris Kennaway error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 623e8aafc91SKris Kennaway error("It is also possible that the %s host key has just been changed.", type); 624e8aafc91SKris Kennaway error("Please contact your system administrator."); 625e8aafc91SKris Kennaway error("Add correct host key in %.100s to get rid of this message.", 626e8aafc91SKris Kennaway user_hostfile); 627e8aafc91SKris Kennaway 628e8aafc91SKris Kennaway /* 629e8aafc91SKris Kennaway * If strict host key checking is in use, the user will have 630e8aafc91SKris Kennaway * to edit the key manually and we can only abort. 631e8aafc91SKris Kennaway */ 632e8aafc91SKris Kennaway if (options.strict_host_key_checking) 633e8aafc91SKris Kennaway fatal("%s host key for %.200s has changed and you have requested strict checking.", type, host); 634e8aafc91SKris Kennaway 635e8aafc91SKris Kennaway /* 636e8aafc91SKris Kennaway * If strict host key checking has not been requested, allow 637e8aafc91SKris Kennaway * the connection but without password authentication or 638e8aafc91SKris Kennaway * agent forwarding. 639e8aafc91SKris Kennaway */ 640e8aafc91SKris Kennaway if (options.password_authentication) { 641e8aafc91SKris Kennaway error("Password authentication is disabled to avoid trojan horses."); 642e8aafc91SKris Kennaway options.password_authentication = 0; 643e8aafc91SKris Kennaway } 644e8aafc91SKris Kennaway if (options.forward_agent) { 645e8aafc91SKris Kennaway error("Agent forwarding is disabled to avoid trojan horses."); 646e8aafc91SKris Kennaway options.forward_agent = 0; 647e8aafc91SKris Kennaway } 648e8aafc91SKris Kennaway /* 649e8aafc91SKris Kennaway * XXX Should permit the user to change to use the new id. 650e8aafc91SKris Kennaway * This could be done by converting the host key to an 651e8aafc91SKris Kennaway * identifying sentence, tell that the host identifies itself 652e8aafc91SKris Kennaway * by that sentence, and ask the user if he/she whishes to 653e8aafc91SKris Kennaway * accept the authentication. 654e8aafc91SKris Kennaway */ 655e8aafc91SKris Kennaway break; 656e8aafc91SKris Kennaway } 657e8aafc91SKris Kennaway if (options.check_host_ip) 658e8aafc91SKris Kennaway xfree(ip); 659e8aafc91SKris Kennaway } 660511b41d2SMark Murray 661fe5fd017SMark Murray #ifdef KRB5 662fe5fd017SMark Murray int 663fe5fd017SMark Murray try_krb5_authentication(krb5_context *context, krb5_auth_context *auth_context) 664fe5fd017SMark Murray { 665fe5fd017SMark Murray krb5_error_code problem; 666fe5fd017SMark Murray const char *tkfile; 667fe5fd017SMark Murray struct stat buf; 668fe5fd017SMark Murray krb5_ccache ccache = NULL; 669fe5fd017SMark Murray const char *remotehost; 670fe5fd017SMark Murray krb5_data ap; 671fe5fd017SMark Murray int type, payload_len; 672fe5fd017SMark Murray krb5_ap_rep_enc_part *reply = NULL; 673fe5fd017SMark Murray int ret; 674fe5fd017SMark Murray 675fe5fd017SMark Murray memset(&ap, 0, sizeof(ap)); 676fe5fd017SMark Murray 677fe5fd017SMark Murray problem = krb5_init_context(context); 678fe5fd017SMark Murray if (problem) { 679fe5fd017SMark Murray ret = 0; 680fe5fd017SMark Murray goto out; 681fe5fd017SMark Murray } 682fe5fd017SMark Murray 683fe5fd017SMark Murray tkfile = krb5_cc_default_name(*context); 684fe5fd017SMark Murray if (strncmp(tkfile, "FILE:", 5) == 0) 685fe5fd017SMark Murray tkfile += 5; 686fe5fd017SMark Murray 687fe5fd017SMark Murray if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) { 688fe5fd017SMark Murray debug("Kerberos V5: could not get default ccache (permission denied)."); 689fe5fd017SMark Murray ret = 0; 690fe5fd017SMark Murray goto out; 691fe5fd017SMark Murray } 692fe5fd017SMark Murray 693fe5fd017SMark Murray problem = krb5_cc_default(*context, &ccache); 694fe5fd017SMark Murray if (problem) { 695fe5fd017SMark Murray ret = 0; 696fe5fd017SMark Murray goto out; 697fe5fd017SMark Murray } 698fe5fd017SMark Murray 699fe5fd017SMark Murray remotehost = get_canonical_hostname(); 700fe5fd017SMark Murray 701aeccfe99SAssar Westerlund problem = krb5_mk_req(*context, auth_context, AP_OPTS_MUTUAL_REQUIRED, 702aeccfe99SAssar Westerlund "host", remotehost, NULL, ccache, &ap); 703fe5fd017SMark Murray if (problem) { 704fe5fd017SMark Murray ret = 0; 705fe5fd017SMark Murray goto out; 706fe5fd017SMark Murray } 707fe5fd017SMark Murray 708cb96ab36SAssar Westerlund packet_start(SSH_CMSG_AUTH_KERBEROS); 709fe5fd017SMark Murray packet_put_string((char *) ap.data, ap.length); 710fe5fd017SMark Murray packet_send(); 711fe5fd017SMark Murray packet_write_wait(); 712fe5fd017SMark Murray 713fe5fd017SMark Murray xfree(ap.data); 714fe5fd017SMark Murray ap.length = 0; 715fe5fd017SMark Murray 716fe5fd017SMark Murray type = packet_read(&payload_len); 717fe5fd017SMark Murray switch (type) { 718fe5fd017SMark Murray case SSH_SMSG_FAILURE: 719cb96ab36SAssar Westerlund /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ 720fe5fd017SMark Murray debug("Kerberos V5 authentication failed."); 721fe5fd017SMark Murray ret = 0; 722fe5fd017SMark Murray break; 723fe5fd017SMark Murray 724cb96ab36SAssar Westerlund case SSH_SMSG_AUTH_KERBEROS_RESPONSE: 725cb96ab36SAssar Westerlund /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ 726fe5fd017SMark Murray debug("Kerberos V5 authentication accepted."); 727fe5fd017SMark Murray 728fe5fd017SMark Murray /* Get server's response. */ 729fe5fd017SMark Murray ap.data = packet_get_string((unsigned int *) &ap.length); 730fe5fd017SMark Murray 731fe5fd017SMark Murray packet_integrity_check(payload_len, 4 + ap.length, type); 732fe5fd017SMark Murray /* XXX je to dobre? */ 733fe5fd017SMark Murray 734fe5fd017SMark Murray problem = krb5_rd_rep(*context, *auth_context, &ap, &reply); 735fe5fd017SMark Murray if (problem) { 736fe5fd017SMark Murray ret = 0; 737fe5fd017SMark Murray } 738fe5fd017SMark Murray ret = 1; 739fe5fd017SMark Murray break; 740fe5fd017SMark Murray 741fe5fd017SMark Murray default: 742fe5fd017SMark Murray packet_disconnect("Protocol error on Kerberos V5 response: %d", type); 743fe5fd017SMark Murray ret = 0; 744fe5fd017SMark Murray break; 745fe5fd017SMark Murray 746fe5fd017SMark Murray } 747fe5fd017SMark Murray 748fe5fd017SMark Murray out: 749fe5fd017SMark Murray if (ccache != NULL) 750fe5fd017SMark Murray krb5_cc_close(*context, ccache); 751fe5fd017SMark Murray if (reply != NULL) 752fe5fd017SMark Murray krb5_free_ap_rep_enc_part(*context, reply); 753fe5fd017SMark Murray if (ap.length > 0) 754fe5fd017SMark Murray krb5_data_free(&ap); 755fe5fd017SMark Murray 756fe5fd017SMark Murray return ret; 757fe5fd017SMark Murray 758fe5fd017SMark Murray } 759fe5fd017SMark Murray 760fe5fd017SMark Murray void 761fe5fd017SMark Murray send_krb5_tgt(krb5_context context, krb5_auth_context auth_context) 762fe5fd017SMark Murray { 763fe5fd017SMark Murray int fd; 764fe5fd017SMark Murray int type, payload_len; 765fe5fd017SMark Murray krb5_error_code problem; 766fe5fd017SMark Murray krb5_data outbuf; 767fe5fd017SMark Murray krb5_ccache ccache = NULL; 768fe5fd017SMark Murray krb5_creds creds; 769fe5fd017SMark Murray krb5_kdc_flags flags; 770fe5fd017SMark Murray const char* remotehost = get_canonical_hostname(); 771fe5fd017SMark Murray 772fe5fd017SMark Murray memset(&creds, 0, sizeof(creds)); 773fe5fd017SMark Murray memset(&outbuf, 0, sizeof(outbuf)); 774fe5fd017SMark Murray 775fe5fd017SMark Murray fd = packet_get_connection_in(); 776fe5fd017SMark Murray problem = krb5_auth_con_setaddrs_from_fd(context, auth_context, &fd); 777fe5fd017SMark Murray if (problem) { 778fe5fd017SMark Murray goto out; 779fe5fd017SMark Murray } 780fe5fd017SMark Murray 781fe5fd017SMark Murray #if 0 782fe5fd017SMark Murray tkfile = krb5_cc_default_name(context); 783fe5fd017SMark Murray if (strncmp(tkfile, "FILE:", 5) == 0) 784fe5fd017SMark Murray tkfile += 5; 785fe5fd017SMark Murray 786fe5fd017SMark Murray if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) { 787fe5fd017SMark Murray debug("Kerberos V5: could not get default ccache (permission denied)."); 788fe5fd017SMark Murray goto out; 789fe5fd017SMark Murray } 790fe5fd017SMark Murray #endif 791fe5fd017SMark Murray 792fe5fd017SMark Murray problem = krb5_cc_default(context, &ccache); 793fe5fd017SMark Murray if (problem) { 794fe5fd017SMark Murray goto out; 795fe5fd017SMark Murray } 796fe5fd017SMark Murray 797fe5fd017SMark Murray problem = krb5_cc_get_principal(context, ccache, &creds.client); 798fe5fd017SMark Murray if (problem) { 799fe5fd017SMark Murray goto out; 800fe5fd017SMark Murray } 801fe5fd017SMark Murray 802fe5fd017SMark Murray problem = krb5_build_principal(context, &creds.server, 803fe5fd017SMark Murray strlen(creds.client->realm), 804fe5fd017SMark Murray creds.client->realm, 805fe5fd017SMark Murray "krbtgt", 806fe5fd017SMark Murray creds.client->realm, 807fe5fd017SMark Murray NULL); 808fe5fd017SMark Murray if (problem) { 809fe5fd017SMark Murray goto out; 810fe5fd017SMark Murray } 811fe5fd017SMark Murray 812fe5fd017SMark Murray creds.times.endtime = 0; 813fe5fd017SMark Murray 814fe5fd017SMark Murray flags.i = 0; 815fe5fd017SMark Murray flags.b.forwarded = 1; 816fe5fd017SMark Murray flags.b.forwardable = krb5_config_get_bool(context, NULL, 817fe5fd017SMark Murray "libdefaults", "forwardable", NULL); 818fe5fd017SMark Murray 819fe5fd017SMark Murray problem = krb5_get_forwarded_creds (context, 820fe5fd017SMark Murray auth_context, 821fe5fd017SMark Murray ccache, 822fe5fd017SMark Murray flags.i, 823fe5fd017SMark Murray remotehost, 824fe5fd017SMark Murray &creds, 825fe5fd017SMark Murray &outbuf); 826fe5fd017SMark Murray if (problem) { 827fe5fd017SMark Murray goto out; 828fe5fd017SMark Murray } 829fe5fd017SMark Murray 830cb96ab36SAssar Westerlund packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); 831fe5fd017SMark Murray packet_put_string((char *)outbuf.data, outbuf.length); 832fe5fd017SMark Murray packet_send(); 833fe5fd017SMark Murray packet_write_wait(); 834fe5fd017SMark Murray 835fe5fd017SMark Murray type = packet_read(&payload_len); 836fe5fd017SMark Murray switch (type) { 837fe5fd017SMark Murray case SSH_SMSG_SUCCESS: 838fe5fd017SMark Murray break; 839fe5fd017SMark Murray case SSH_SMSG_FAILURE: 840fe5fd017SMark Murray break; 841fe5fd017SMark Murray default: 842fe5fd017SMark Murray break; 843fe5fd017SMark Murray } 844fe5fd017SMark Murray 845fe5fd017SMark Murray out: 846fe5fd017SMark Murray if (creds.client) 847fe5fd017SMark Murray krb5_free_principal(context, creds.client); 848fe5fd017SMark Murray if (creds.server) 849fe5fd017SMark Murray krb5_free_principal(context, creds.server); 850fe5fd017SMark Murray if (ccache) 851fe5fd017SMark Murray krb5_cc_close(context, ccache); 852fe5fd017SMark Murray if (outbuf.data) 853fe5fd017SMark Murray xfree(outbuf.data); 854fe5fd017SMark Murray 855fe5fd017SMark Murray return; 856fe5fd017SMark Murray } 857fe5fd017SMark Murray #endif /* KRB5 */ 858fe5fd017SMark Murray 859511b41d2SMark Murray /* 860511b41d2SMark Murray * Starts a dialog with the server, and authenticates the current user on the 861511b41d2SMark Murray * server. This does not need any extra privileges. The basic connection 862511b41d2SMark Murray * to the server must already have been established before this is called. 863511b41d2SMark Murray * If login fails, this function prints an error and never returns. 864511b41d2SMark Murray * This function does not require super-user privileges. 865511b41d2SMark Murray */ 866511b41d2SMark Murray void 867511b41d2SMark Murray ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, 868511b41d2SMark Murray struct sockaddr *hostaddr, uid_t original_real_uid) 869511b41d2SMark Murray { 870e8aafc91SKris Kennaway struct passwd *pw; 871511b41d2SMark Murray char *host, *cp; 872e8aafc91SKris Kennaway char *server_user, *local_user; 873e8aafc91SKris Kennaway 874e8aafc91SKris Kennaway /* Get local user name. Use it as server user if no user name was given. */ 875e8aafc91SKris Kennaway pw = getpwuid(original_real_uid); 876e8aafc91SKris Kennaway if (!pw) 877c2d3a559SKris Kennaway fatal("User id %u not found from user database.", original_real_uid); 878e8aafc91SKris Kennaway local_user = xstrdup(pw->pw_name); 879e8aafc91SKris Kennaway server_user = options.user ? options.user : local_user; 880511b41d2SMark Murray 881511b41d2SMark Murray /* Convert the user-supplied hostname into all lowercase. */ 882511b41d2SMark Murray host = xstrdup(orighost); 883511b41d2SMark Murray for (cp = host; *cp; cp++) 884511b41d2SMark Murray if (isupper(*cp)) 885511b41d2SMark Murray *cp = tolower(*cp); 886511b41d2SMark Murray 887511b41d2SMark Murray /* Exchange protocol version identification strings with the server. */ 888511b41d2SMark Murray ssh_exchange_identification(); 889511b41d2SMark Murray 890511b41d2SMark Murray /* Put the connection into non-blocking mode. */ 891511b41d2SMark Murray packet_set_nonblocking(); 892511b41d2SMark Murray 893511b41d2SMark Murray /* key exchange */ 894511b41d2SMark Murray /* authenticate user */ 895e8aafc91SKris Kennaway if (compat20) { 896e8aafc91SKris Kennaway ssh_kex2(host, hostaddr); 897e8aafc91SKris Kennaway ssh_userauth2(server_user, host); 898e8aafc91SKris Kennaway } else { 899e8aafc91SKris Kennaway ssh_kex(host, hostaddr); 900e8aafc91SKris Kennaway ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key); 901e8aafc91SKris Kennaway } 902511b41d2SMark Murray } 903e0fbb1d2SBrian Feldman 904e0fbb1d2SBrian Feldman void 905e0fbb1d2SBrian Feldman ssh_put_password(char *password) 906e0fbb1d2SBrian Feldman { 907e0fbb1d2SBrian Feldman int size; 908e0fbb1d2SBrian Feldman char *padded; 909e0fbb1d2SBrian Feldman 910e0fbb1d2SBrian Feldman size = roundup(strlen(password) + 1, 32); 911e0fbb1d2SBrian Feldman padded = xmalloc(size); 912e0fbb1d2SBrian Feldman memset(padded, 0, size); 913e0fbb1d2SBrian Feldman strlcpy(padded, password, size); 914e0fbb1d2SBrian Feldman packet_put_string(padded, size); 915e0fbb1d2SBrian Feldman memset(padded, 0, size); 916e0fbb1d2SBrian Feldman xfree(padded); 917e0fbb1d2SBrian Feldman } 918