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