sshconnect.c revision 74197
1139826Simp/* 253541Sshin * Author: Tatu Ylonen <ylo@cs.hut.fi> 353541Sshin * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 453541Sshin * All rights reserved 553541Sshin * Code to connect to a remote host, and to perform the client side of the 653541Sshin * login (authentication) dialog. 753541Sshin * 853541Sshin * As far as I am concerned, the code I have written for this software 953541Sshin * can be used freely for any purpose. Any derived versions of this 1053541Sshin * software must be clearly marked as such, and if the derived work is 1153541Sshin * incompatible with the protocol description in the RFC file, it must be 1253541Sshin * called by a name other than "ssh" or "Secure Shell". 1353541Sshin */ 1453541Sshin 1553541Sshin#include "includes.h" 1653541SshinRCSID("$OpenBSD: sshconnect.c,v 1.79 2000/09/17 15:52:51 markus Exp $"); 1753541SshinRCSID("$FreeBSD: head/crypto/openssh/sshconnect.c 74197 2001-03-13 04:42:38Z assar $"); 1853541Sshin 1953541Sshin#include <openssl/bn.h> 2053541Sshin#include <openssl/dsa.h> 2153541Sshin#include <openssl/rsa.h> 2253541Sshin 2353541Sshin#include "xmalloc.h" 2453541Sshin#include "rsa.h" 2553541Sshin#include "ssh.h" 2653541Sshin#include "buffer.h" 2753541Sshin#include "packet.h" 2853541Sshin#include "uidswap.h" 2953541Sshin#include "compat.h" 30139826Simp#include "readconf.h" 3153541Sshin#include "key.h" 32180305Srwatson#include "sshconnect.h" 33180305Srwatson#include "hostfile.h" 3453541Sshin 3553541Sshinchar *client_version_string = NULL; 3653541Sshinchar *server_version_string = NULL; 3753541Sshin 3853541Sshinextern Options options; 3953541Sshinextern char *__progname; 4053541Sshin 4153541Sshin/* 4253541Sshin * Connect to the given ssh server using a proxy command. 4353541Sshin */ 4453541Sshinint 4553541Sshinssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, 4653541Sshin const char *proxy_command) 4753541Sshin{ 4853541Sshin Buffer command; 4953541Sshin const char *cp; 5053541Sshin char *command_string; 5153541Sshin int pin[2], pout[2]; 5253541Sshin pid_t pid; 5353541Sshin char strport[NI_MAXSERV]; 5453541Sshin 5553541Sshin /* Convert the port number into a string. */ 5653541Sshin snprintf(strport, sizeof strport, "%hu", port); 5753541Sshin 5853541Sshin /* Build the final command string in the buffer by making the 5953541Sshin appropriate substitutions to the given proxy command. */ 6053541Sshin buffer_init(&command); 6153541Sshin for (cp = proxy_command; *cp; cp++) { 62174510Sobrien if (cp[0] == '%' && cp[1] == '%') { 63174510Sobrien buffer_append(&command, "%", 1); 64174510Sobrien cp++; 6555009Sshin continue; 6678064Sume } 6755009Sshin if (cp[0] == '%' && cp[1] == 'h') { 6853541Sshin buffer_append(&command, host, strlen(host)); 6995759Stanimura cp++; 70185435Sbz continue; 71253085Sae } 7295759Stanimura if (cp[0] == '%' && cp[1] == 'p') { 7353541Sshin buffer_append(&command, strport, strlen(strport)); 7495759Stanimura cp++; 75170689Srwatson continue; 7653541Sshin } 7795759Stanimura buffer_append(&command, cp, 1); 7895759Stanimura } 7953541Sshin buffer_append(&command, "\0", 1); 8053541Sshin 8195759Stanimura /* Get the final command string. */ 82148385Sume command_string = buffer_ptr(&command); 8353541Sshin 8453541Sshin /* Create pipes for communicating with the proxy. */ 8595759Stanimura if (pipe(pin) < 0 || pipe(pout) < 0) 8653541Sshin fatal("Could not create pipes to communicate with the proxy: %.100s", 87185571Sbz strerror(errno)); 8853541Sshin 8953541Sshin debug("Executing proxy command: %.500s", command_string); 9053541Sshin 9153541Sshin /* Fork and execute the proxy command. */ 92185571Sbz if ((pid = fork()) == 0) { 93185571Sbz char *argv[10]; 9495759Stanimura 9562587Sitojun /* Child. Permanently give up superuser privileges. */ 96211501Sanchie permanently_set_uid(original_real_uid); 9795759Stanimura 9856723Sshin /* Redirect stdin and stdout. */ 9953541Sshin close(pin[1]); 10095759Stanimura if (pin[0] != 0) { 10153541Sshin if (dup2(pin[0], 0) < 0) 10295759Stanimura perror("dup2 stdin"); 10362587Sitojun close(pin[0]); 104211501Sanchie } 10553541Sshin close(pout[0]); 106171167Sgnn if (dup2(pout[1], 1) < 0) 107105199Ssam perror("dup2 stdout"); 108105199Ssam /* Cannot be 1 because pin allocated two descriptors. */ 109171167Sgnn close(pout[1]); 110105199Ssam 11153541Sshin /* Stderr is left as it is so that error messages get 11253541Sshin printed on the user's terminal. */ 11353541Sshin argv[0] = "/bin/sh"; 11453541Sshin argv[1] = "-c"; 11553541Sshin argv[2] = command_string; 11653541Sshin argv[3] = NULL; 11753541Sshin 11853541Sshin /* Execute the proxy command. Note that we gave up any 11953541Sshin extra privileges above. */ 120195699Srwatson execv("/bin/sh", argv); 121195699Srwatson perror("/bin/sh"); 122195727Srwatson exit(1); 123195727Srwatson } 124185348Szec /* Parent. */ 12553541Sshin if (pid < 0) 12653541Sshin fatal("fork failed: %.100s", strerror(errno)); 12753541Sshin 128253085Sae /* Close child side of the descriptors. */ 129253085Sae close(pin[0]); 130207369Sbz close(pout[1]); 131253085Sae 132253085Sae /* Free the command name. */ 133253085Sae buffer_free(&command); 134253085Sae 13553541Sshin /* Set the connection file descriptors. */ 136191672Sbms packet_set_connection(pout[0], pin[1]); 137191672Sbms 138166938Sbms return 1; 139191672Sbms} 140191672Sbms 141191672Sbms/* 142191672Sbms * Creates a (possibly privileged) socket for use as the ssh connection. 143195699Srwatson */ 144191672Sbmsint 145191672Sbmsssh_create_socket(uid_t original_real_uid, int privileged, int family) 146191672Sbms{ 147191672Sbms int sock; 148166938Sbms 149166938Sbms /* 150166938Sbms * If we are running as root and want to connect to a privileged 151166938Sbms * port, bind our own socket to a privileged port. 152194581Srdivacky */ 153166938Sbms if (privileged) { 154166938Sbms int p = IPPORT_RESERVED - 1; 155180305Srwatson sock = rresvport_af(&p, family); 156180305Srwatson if (sock < 0) 15753541Sshin error("rresvport: af=%d %.100s", family, strerror(errno)); 15853541Sshin else 159171259Sdelphij debug("Allocated local port %d.", p); 16053541Sshin } else { 161191672Sbms /* 16253541Sshin * Just create an ordinary socket on arbitrary port. We use 16353541Sshin * the user's uid to create the socket. 16453541Sshin */ 16553541Sshin temporarily_use_uid(original_real_uid); 16678064Sume sock = socket(family, SOCK_STREAM, 0); 167121901Sume if (sock < 0) 16853541Sshin error("socket: %.100s", strerror(errno)); 169252007Sae restore_uid(); 17078064Sume } 17183934Sbrooks return sock; 172180305Srwatson} 17378064Sume 174180305Srwatson/* 17553541Sshin * Opens a TCP/IP connection to the remote server on the given host. 17678064Sume * The canonical host name used to connect will be returned in *host. 177121901Sume * The address of the remote host will be returned in hostaddr. 17853541Sshin * If port is 0, the default port will be used. If anonymous is zero, 179191672Sbms * a privileged port will be allocated to make the connection. 180191672Sbms * This requires super-user privileges if anonymous is false. 181181803Sbz * Connection_attempts specifies the maximum number of tries (one per 182181803Sbz * second). If proxy_command is non-NULL, it specifies the command (with %h 183185435Sbz * and %p substituted for host and port, respectively) to use to contact 184186141Sbz * the daemon. 18553541Sshin */ 186186141Sbzint 187186141Sbzssh_connect(char **host, struct sockaddr_storage * hostaddr, 188180850Smav u_short port, int connection_attempts, 18953541Sshin int anonymous, uid_t original_real_uid, 19053541Sshin const char *proxy_command) 191180850Smav{ 19253541Sshin int sock = -1, attempt; 19353541Sshin struct servent *sp; 194180850Smav struct addrinfo hints, *ai, *aitop; 195200473Sbz char ntop[NI_MAXHOST], strport[NI_MAXSERV]; 196191672Sbms int gaierr; 197191672Sbms struct linger linger; 198191672Sbms 199191672Sbms debug("ssh_connect: getuid %u geteuid %u anon %d", 200191672Sbms (u_int) getuid(), (u_int) geteuid(), anonymous); 201191672Sbms 202191672Sbms /* Get default port if port has not been set. */ 203191672Sbms if (port == 0) { 204191672Sbms sp = getservbyname(SSH_SERVICE_NAME, "tcp"); 205191672Sbms if (sp) 206248180Sae port = ntohs(sp->s_port); 20778064Sume else 208252007Sae port = SSH_DEFAULT_PORT; 209151459Ssuz } 21078064Sume /* If a proxy command is given, connect using it. */ 211180932Smav if (proxy_command != NULL) 212252007Sae return ssh_proxy_connect(*host, port, original_real_uid, proxy_command); 213180850Smav 21478064Sume /* No proxy command. */ 21553541Sshin 216191672Sbms memset(&hints, 0, sizeof(hints)); 217191672Sbms hints.ai_family = IPv4or6; 218191672Sbms hints.ai_socktype = SOCK_STREAM; 219191672Sbms hints.ai_flags = AI_CANONNAME; 220191672Sbms snprintf(strport, sizeof strport, "%d", port); 221191672Sbms if ((gaierr = getaddrinfo(*host, strport, &hints, &aitop)) != 0) 222191672Sbms fatal("%s: %.100s: %s", __progname, *host, 223191672Sbms gai_strerror(gaierr)); 224199518Sbms 225199518Sbms /* 226199518Sbms * Try to connect several times. On some machines, the first time 227199518Sbms * will sometimes fail. In general socket code appears to behave 228199518Sbms * quite magically on many machines. 229199518Sbms */ 230199518Sbms for (attempt = 0; attempt < connection_attempts; attempt++) { 231199518Sbms if (attempt > 0) 232199518Sbms debug("Trying again..."); 233199518Sbms 234199518Sbms /* Loop through addresses for this host, and try each one in 235199518Sbms sequence until the connection succeeds. */ 236199518Sbms for (ai = aitop; ai; ai = ai->ai_next) { 237199518Sbms if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) 238199518Sbms continue; 239199518Sbms if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 240199518Sbms ntop, sizeof(ntop), strport, sizeof(strport), 241191672Sbms NI_NUMERICHOST|NI_NUMERICSERV) != 0) { 242191672Sbms error("ssh_connect: getnameinfo failed"); 243199518Sbms continue; 244199518Sbms } 245199518Sbms debug("Connecting to %.200s [%.100s] port %s.", 246191672Sbms ai->ai_canonname, ntop, strport); 247199518Sbms 248199518Sbms /* Create a socket for connecting. */ 249199518Sbms sock = ssh_create_socket(original_real_uid, 250199518Sbms !anonymous && geteuid() == 0 && port < IPPORT_RESERVED, 251199518Sbms ai->ai_family); 252199518Sbms if (sock < 0) 253199518Sbms continue; 254199518Sbms 255199518Sbms /* Connect to the host. We use the user's uid in the 256199518Sbms * hope that it will help with tcp_wrappers showing 257191672Sbms * the remote uid as root. 258191672Sbms */ 259211301Sbz temporarily_use_uid(original_real_uid); 260191672Sbms if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { 261191672Sbms /* Successful connection. */ 262191672Sbms memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); 263186163Skmacy restore_uid(); 26453541Sshin break; 26578064Sume } else { 266171167Sgnn debug("connect: %.100s", strerror(errno)); 26778064Sume restore_uid(); 26878064Sume /* 26978064Sume * Close the failed socket; there appear to 270125396Sume * be some problems when reusing a socket for 27178064Sume * which connect() has already returned an 272253571Sae * error. 273180305Srwatson */ 274105199Ssam shutdown(sock, SHUT_RDWR); 275171167Sgnn close(sock); 27653541Sshin } 277186223Sbz } 278186141Sbz if (ai) { 279121674Sume#if 0 28053541Sshin if (ai->ai_canonname != NULL) 28153541Sshin *host = xstrdup(ai->ai_canonname); 282186141Sbz#endif 283121901Sume break; /* Successful connection. */ 28453541Sshin } 28553541Sshin 28653541Sshin /* Sleep a moment before retrying. */ 28753541Sshin sleep(1); 288252007Sae } 28997658Stanimura 290186141Sbz freeaddrinfo(aitop); 29153541Sshin 29253541Sshin /* Return failure if we didn't get a successful connection. */ 293178377Srwatson if (attempt >= connection_attempts) 29453541Sshin return 0; 29553541Sshin 29653541Sshin debug("Connection established."); 297181803Sbz 298171167Sgnn /* 29978064Sume * Set socket options. We would like the socket to disappear as soon 30078064Sume * as it has been closed for whatever reason. 30178064Sume */ 302186170Skmacy /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ 30378064Sume linger.l_onoff = 1; 304253571Sae linger.l_linger = 5; 305249294Sae setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); 306180305Srwatson 307178377Srwatson /* Set the connection. */ 308105199Ssam packet_set_connection(sock, sock); 309171167Sgnn 310186163Skmacy return 1; 311186223Sbz} 312186141Sbz 313121674Sume/* 314180305Srwatson * Waits for the server identification string, and sends our own 31553541Sshin * identification string. 316186141Sbz */ 317180305Srwatsonvoid 31853541Sshinssh_exchange_identification() 31953541Sshin{ 32053541Sshin char buf[256], remote_version[256]; /* must be same size! */ 321252007Sae int remote_major, remote_minor, i, mismatch; 32297658Stanimura int connection_in = packet_get_connection_in(); 323186141Sbz int connection_out = packet_get_connection_out(); 324178377Srwatson 32553541Sshin /* Read other side\'s version identification. */ 326252007Sae for (;;) { 32778064Sume for (i = 0; i < sizeof(buf) - 1; i++) { 328252007Sae int len = atomicio(read, connection_in, &buf[i], 1); 32953541Sshin if (len < 0) 33053541Sshin fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); 33153541Sshin if (len != 1) 33253541Sshin fatal("ssh_exchange_identification: Connection closed by remote host"); 33353541Sshin if (buf[i] == '\r') { 334180305Srwatson buf[i] = '\n'; 335180305Srwatson buf[i + 1] = 0; 33653541Sshin continue; /**XXX wait for \n */ 337249294Sae } 33853541Sshin if (buf[i] == '\n') { 339180305Srwatson buf[i + 1] = 0; 34053541Sshin break; 34153541Sshin } 34262587Sitojun } 343171259Sdelphij buf[sizeof(buf) - 1] = 0; 34462587Sitojun if (strncmp(buf, "SSH-", 4) == 0) 34562587Sitojun break; 34662587Sitojun debug("ssh_exchange_identification: %s", buf); 34762587Sitojun } 34878064Sume server_version_string = xstrdup(buf); 34978064Sume 350125776Sume /* 351175162Sobrien * Check that the versions match. In future this might accept 35262587Sitojun * several versions and set appropriate flags to handle them. 35362587Sitojun */ 35462587Sitojun if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", 35562587Sitojun &remote_major, &remote_minor, remote_version) != 3) 35662587Sitojun fatal("Bad remote protocol version identification: '%.100s'", buf); 35762587Sitojun debug("Remote protocol version %d.%d, remote software version %.100s", 35862587Sitojun remote_major, remote_minor, remote_version); 35962587Sitojun 36062587Sitojun compat_datafellows(remote_version); 36162587Sitojun mismatch = 0; 36262587Sitojun 36362587Sitojun switch(remote_major) { 36462587Sitojun case 1: 36562587Sitojun if (remote_minor == 99 && 366180305Srwatson (options.protocol & SSH_PROTO_2) && 367180305Srwatson !(options.protocol & SSH_PROTO_1_PREFERRED)) { 368180305Srwatson enable_compat20(); 36962587Sitojun break; 37078064Sume } 37162587Sitojun if (!(options.protocol & SSH_PROTO_1)) { 37262587Sitojun mismatch = 1; 37362587Sitojun break; 374125776Sume } 37578064Sume if (remote_minor < 3) { 37662587Sitojun fatal("Remote machine has too old SSH software version."); 37762587Sitojun } else if (remote_minor == 3) { 37862587Sitojun /* We speak 1.3, too. */ 379125776Sume enable_compat13(); 38078064Sume if (options.forward_agent) { 38162587Sitojun log("Agent forwarding disabled for protocol 1.3"); 38262587Sitojun options.forward_agent = 0; 383181803Sbz } 384180305Srwatson } 38562587Sitojun break; 38662587Sitojun case 2: 38753541Sshin if (options.protocol & SSH_PROTO_2) { 388180305Srwatson enable_compat20(); 389180305Srwatson break; 39053541Sshin } 39153541Sshin /* FALLTHROUGH */ 39253541Sshin default: 39353541Sshin mismatch = 1; 39453541Sshin break; 39553541Sshin } 39653541Sshin if (mismatch) 39753541Sshin fatal("Protocol major versions differ: %d vs. %d", 39853541Sshin (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 39953541Sshin remote_major); 400120941Sume if (compat20) 401211501Sanchie packet_set_ssh2_format(); 40253541Sshin /* Send our own protocol version identification. */ 40353541Sshin snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", 40453541Sshin compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, 40553541Sshin compat20 ? PROTOCOL_MINOR_2 : PROTOCOL_MINOR_1, 40653541Sshin SSH_VERSION); 40753541Sshin if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) 40853541Sshin fatal("write: %.100s", strerror(errno)); 409148247Sume client_version_string = xstrdup(buf); 41053541Sshin chop(client_version_string); 41153541Sshin chop(server_version_string); 412148385Sume debug("Local version string %.100s", client_version_string); 413211435Sume} 414194777Sbz 41553541Sshinint 41653541Sshinread_yes_or_no(const char *prompt, int defval) 41753541Sshin{ 41853541Sshin char buf[1024]; 41953541Sshin FILE *f; 42053541Sshin int retval = -1; 42153541Sshin 42253541Sshin if (isatty(0)) 423186141Sbz f = stdin; 424178285Srwatson else 42553541Sshin f = fopen("/dev/tty", "rw"); 42653541Sshin 427186170Skmacy if (f == NULL) 428148242Sume return 0; 429175630Sbz 430175630Sbz fflush(stdout); 43153541Sshin 432121472Sume while (1) { 433148247Sume fprintf(stderr, "%s", prompt); 434148247Sume if (fgets(buf, sizeof(buf), f) == NULL) { 435148247Sume /* Print a newline (the prompt probably didn\'t have one). */ 43653541Sshin fprintf(stderr, "\n"); 43753541Sshin strlcpy(buf, "no", sizeof buf); 438148385Sume } 439180305Srwatson /* Remove newline from response. */ 440148385Sume if (strchr(buf, '\n')) 441148385Sume *strchr(buf, '\n') = 0; 442148385Sume 443211530Sume if (buf[0] == 0) 444211530Sume retval = defval; 445211435Sume if (strcmp(buf, "yes") == 0) 446211435Sume retval = 1; 447148385Sume else if (strcmp(buf, "no") == 0) 448211435Sume retval = 0; 449148385Sume else 450148385Sume fprintf(stderr, "Please type 'yes' or 'no'.\n"); 451148385Sume 452148385Sume if (retval != -1) { 453180305Srwatson if (f != stdin) 454180305Srwatson fclose(f); 45553541Sshin return retval; 45653541Sshin } 45753541Sshin } 45853541Sshin} 45953541Sshin 46053541Sshin/* 46153541Sshin * check whether the supplied host key is valid, return only if ok. 46253541Sshin */ 46353541Sshin 46453541Sshinvoid 46553541Sshincheck_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, 46653541Sshin const char *user_hostfile, const char *system_hostfile) 46753541Sshin{ 468243882Sglebius Key *file_key; 469133592Srwatson char *type = key_type(host_key); 470133592Srwatson char *ip = NULL; 471133592Srwatson char hostline[1000], *hostp; 472133592Srwatson HostStatus host_status; 47353541Sshin HostStatus ip_status; 47453541Sshin int local = 0, host_ip_differ = 0; 47553541Sshin char ntop[NI_MAXHOST]; 47653541Sshin 47753541Sshin /* 478194777Sbz * Force accepting of the host key for loopback/localhost. The 479194777Sbz * problem is that if the home directory is NFS-mounted to multiple 480194777Sbz * machines, localhost will refer to a different machine in each of 481121472Sume * them, and the user will get bogus HOST_CHANGED warnings. This 482207277Sbz * essentially disables host authentication for localhost; however, 483188144Sjamie * this is probably not a real problem. 484188144Sjamie */ 485194777Sbz /** hostaddr == 0! */ 486148385Sume switch (hostaddr->sa_family) { 487148385Sume case AF_INET: 488148385Sume local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; 489148385Sume break; 490148385Sume case AF_INET6: 491148385Sume local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); 492148385Sume break; 493148385Sume default: 494148385Sume local = 0; 495148385Sume break; 496148385Sume } 497148385Sume if (local) { 498148385Sume debug("Forcing accepting of host key for loopback/localhost."); 499148385Sume return; 500148385Sume } 501148385Sume 502180305Srwatson /* 503180305Srwatson * Turn off check_host_ip for proxy connects, since 504180305Srwatson * we don't have the remote ip-address 50555009Sshin */ 506186141Sbz if (options.proxy_command != NULL && options.check_host_ip) 50755009Sshin options.check_host_ip = 0; 508180305Srwatson 509180305Srwatson if (options.check_host_ip) { 510180305Srwatson if (getnameinfo(hostaddr, hostaddr->sa_len, ntop, sizeof(ntop), 511180305Srwatson NULL, 0, NI_NUMERICHOST) != 0) 512180305Srwatson fatal("check_host_key: getnameinfo failed"); 513186141Sbz ip = xstrdup(ntop); 51453541Sshin } 51553541Sshin 51653541Sshin /* 51753541Sshin * Store the host key from the known host file in here so that we can 51853541Sshin * compare it with the key for the IP address. 51953541Sshin */ 52053541Sshin file_key = key_new(host_key->type); 52153541Sshin 522180305Srwatson /* 52353541Sshin * Check if the host key is present in the user\'s list of known 52453541Sshin * hosts or in the systemwide list. 52553541Sshin */ 52653541Sshin host_status = check_host_in_hostfile(user_hostfile, host, host_key, file_key); 52753541Sshin if (host_status == HOST_NEW) 52853541Sshin host_status = check_host_in_hostfile(system_hostfile, host, host_key, file_key); 52953541Sshin /* 53053541Sshin * Also perform check for the ip address, skip the check if we are 53153541Sshin * localhost or the hostname was an ip address to begin with 53253541Sshin */ 53353541Sshin if (options.check_host_ip && !local && strcmp(host, ip)) { 53453541Sshin Key *ip_key = key_new(host_key->type); 53553541Sshin ip_status = check_host_in_hostfile(user_hostfile, ip, host_key, ip_key); 53653541Sshin 53753541Sshin if (ip_status == HOST_NEW) 53853541Sshin ip_status = check_host_in_hostfile(system_hostfile, ip, host_key, ip_key); 53953541Sshin if (host_status == HOST_CHANGED && 54053541Sshin (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) 54153541Sshin host_ip_differ = 1; 54253541Sshin 54353541Sshin key_free(ip_key); 54453541Sshin } else 545211501Sanchie ip_status = host_status; 546211501Sanchie 547211501Sanchie key_free(file_key); 548211501Sanchie 549211501Sanchie switch (host_status) { 550211501Sanchie case HOST_OK: 551211501Sanchie /* The host is known and the key matches. */ 552211501Sanchie debug("Host '%.200s' is known and matches the %s host key.", 553211501Sanchie host, type); 554211501Sanchie if (options.check_host_ip) { 555211501Sanchie if (ip_status == HOST_NEW) { 556211501Sanchie if (!add_host_to_hostfile(user_hostfile, ip, host_key)) 557211501Sanchie log("Failed to add the %s host key for IP address '%.30s' to the list of known hosts (%.30s).", 558211501Sanchie type, ip, user_hostfile); 559211501Sanchie else 560211501Sanchie log("Warning: Permanently added the %s host key for IP address '%.30s' to the list of known hosts.", 561211501Sanchie type, ip); 562148247Sume } else if (ip_status != HOST_OK) 56353541Sshin log("Warning: the %s host key for '%.200s' differs from the key for the IP address '%.30s'", 56453541Sshin type, host, ip); 56553541Sshin } 566190964Srwatson break; 56778064Sume case HOST_NEW: 568252007Sae /* The host is new. */ 56953541Sshin if (options.strict_host_key_checking == 1) { 57053541Sshin /* User has requested strict host key checking. We will not add the host key 57153541Sshin automatically. The only alternative left is to abort. */ 57253541Sshin fatal("No %s host key is known for %.200s and you have requested strict checking.", type, host); 57353541Sshin } else if (options.strict_host_key_checking == 2) { 57453541Sshin /* The default */ 57553541Sshin char prompt[1024]; 57653541Sshin char *fp = key_fingerprint(host_key); 577186170Skmacy snprintf(prompt, sizeof(prompt), 578148247Sume "The authenticity of host '%.200s' can't be established.\n" 57953541Sshin "%s key fingerprint is %s.\n" 58078064Sume "Are you sure you want to continue connecting (yes/no)? ", 581178285Srwatson host, type, fp); 582120856Sume if (!read_yes_or_no(prompt, -1)) 58353541Sshin fatal("Aborted by user!\n"); 58453541Sshin } 58553541Sshin if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) { 58653541Sshin snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); 58753541Sshin hostp = hostline; 58853541Sshin } else 589171259Sdelphij hostp = host; 59053541Sshin 591231852Sbz /* If not in strict mode, add the key automatically to the local known_hosts file. */ 59253541Sshin if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) 59353541Sshin log("Failed to add the host to the list of known hosts (%.500s).", 59453541Sshin user_hostfile); 59553541Sshin else 59653541Sshin log("Warning: Permanently added '%.200s' (%s) to the list of known hosts.", 59753541Sshin hostp, type); 59853541Sshin break; 599120856Sume case HOST_CHANGED: 600231852Sbz if (options.check_host_ip && host_ip_differ) { 601231852Sbz char *msg; 602231852Sbz if (ip_status == HOST_NEW) 603231852Sbz msg = "is unknown"; 604231852Sbz else if (ip_status == HOST_OK) 605231852Sbz msg = "is unchanged"; 606231852Sbz else 607231852Sbz msg = "has a different value"; 608231852Sbz error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 60953541Sshin error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); 610231852Sbz error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 61153541Sshin error("The %s host key for %s has changed,", type, host); 61253541Sshin error("and the key for the according IP address %s", ip); 61353541Sshin error("%s. This could either mean that", msg); 61453541Sshin error("DNS SPOOFING is happening or the IP address for the host"); 61553541Sshin error("and its host key have changed at the same time"); 61653541Sshin } 61756723Sshin /* The host key has changed. */ 61856723Sshin error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 61956723Sshin error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); 62056723Sshin error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); 62156723Sshin error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); 62256723Sshin error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); 62356723Sshin error("It is also possible that the %s host key has just been changed.", type); 624166938Sbms error("Please contact your system administrator."); 625166938Sbms error("Add correct host key in %.100s to get rid of this message.", 62656723Sshin user_hostfile); 627121578Sume 628121578Sume /* 629121578Sume * If strict host key checking is in use, the user will have 63053541Sshin * to edit the key manually and we can only abort. 63153541Sshin */ 63253541Sshin if (options.strict_host_key_checking) 63353541Sshin fatal("%s host key for %.200s has changed and you have requested strict checking.", type, host); 63453541Sshin 63553541Sshin /* 63653541Sshin * If strict host key checking has not been requested, allow 63753541Sshin * the connection but without password authentication or 63856723Sshin * agent forwarding. 63956723Sshin */ 64056723Sshin if (options.password_authentication) { 64156723Sshin error("Password authentication is disabled to avoid trojan horses."); 64256723Sshin options.password_authentication = 0; 64356723Sshin } 64456723Sshin if (options.forward_agent) { 645166938Sbms error("Agent forwarding is disabled to avoid trojan horses."); 646166938Sbms options.forward_agent = 0; 64756723Sshin } 648121578Sume /* 649121578Sume * XXX Should permit the user to change to use the new id. 650121578Sume * This could be done by converting the host key to an 65153541Sshin * identifying sentence, tell that the host identifies itself 65253541Sshin * by that sentence, and ask the user if he/she whishes to 65353541Sshin * accept the authentication. 65453541Sshin */ 65553541Sshin break; 65653541Sshin } 65753541Sshin if (options.check_host_ip) 65853541Sshin xfree(ip); 65953541Sshin} 66053541Sshin 66153541Sshin#ifdef KRB5 66283366Sjulianint 66353541Sshintry_krb5_authentication(krb5_context *context, krb5_auth_context *auth_context) 66453541Sshin{ 665144261Ssam krb5_error_code problem; 666157676Srwatson const char *tkfile; 66753541Sshin struct stat buf; 66853541Sshin krb5_ccache ccache = NULL; 669157374Srwatson const char *remotehost; 670180305Srwatson krb5_data ap; 671175630Sbz int type, payload_len; 672175630Sbz krb5_ap_rep_enc_part *reply = NULL; 673180305Srwatson int ret; 67455009Sshin 675157374Srwatson memset(&ap, 0, sizeof(ap)); 676180305Srwatson 677184214Sdes problem = krb5_init_context(context); 678157374Srwatson if (problem) { 679180305Srwatson ret = 0; 680181803Sbz goto out; 681181803Sbz } 682132714Srwatson 683181803Sbz tkfile = krb5_cc_default_name(*context); 684184205Sdes if (strncmp(tkfile, "FILE:", 5) == 0) 685180305Srwatson tkfile += 5; 686132714Srwatson 68753541Sshin if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) { 688181803Sbz debug("Kerberos V5: could not get default ccache (permission denied)."); 68953541Sshin ret = 0; 690186141Sbz goto out; 69153541Sshin } 69253541Sshin 693144261Ssam problem = krb5_cc_default(*context, &ccache); 69453541Sshin if (problem) { 695178285Srwatson ret = 0; 696180305Srwatson goto out; 69753541Sshin } 69853541Sshin 699157370Srwatson remotehost = get_canonical_hostname(); 70053541Sshin 70153541Sshin problem = krb5_mk_req(*context, auth_context, AP_OPTS_MUTUAL_REQUIRED, 70253541Sshin "host", remotehost, NULL, ccache, &ap); 70353541Sshin if (problem) { 70453541Sshin ret = 0; 705157374Srwatson goto out; 706160549Srwatson } 707191672Sbms 708166938Sbms packet_start(SSH_CMSG_AUTH_KERBEROS); 70953541Sshin packet_put_string((char *) ap.data, ap.length); 710181803Sbz packet_send(); 711178285Srwatson packet_write_wait(); 712184205Sdes 713185344Sbz xfree(ap.data); 714185370Sbz ap.length = 0; 715181803Sbz 71653541Sshin type = packet_read(&payload_len); 71753541Sshin switch (type) { 718160549Srwatson case SSH_SMSG_FAILURE: 719157366Srwatson /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ 72053541Sshin debug("Kerberos V5 authentication failed."); 72153541Sshin ret = 0; 722160549Srwatson break; 723160549Srwatson 724160549Srwatson case SSH_SMSG_AUTH_KERBEROS_RESPONSE: 725160549Srwatson /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ 726160549Srwatson debug("Kerberos V5 authentication accepted."); 72753541Sshin 72853541Sshin /* Get server's response. */ 72953541Sshin ap.data = packet_get_string((unsigned int *) &ap.length); 730160549Srwatson 731160549Srwatson packet_integrity_check(payload_len, 4 + ap.length, type); 732160549Srwatson /* XXX je to dobre? */ 733160549Srwatson 734160549Srwatson problem = krb5_rd_rep(*context, *auth_context, &ap, &reply); 735160549Srwatson if (problem) { 736160549Srwatson ret = 0; 737160549Srwatson } 738160549Srwatson ret = 1; 739160549Srwatson break; 740160549Srwatson 74153541Sshin default: 74253541Sshin packet_disconnect("Protocol error on Kerberos V5 response: %d", type); 74353541Sshin ret = 0; 744180305Srwatson break; 74553541Sshin 746180305Srwatson } 747180305Srwatson 748180305Srwatsonout: 74997658Stanimura if (ccache != NULL) 750180305Srwatson krb5_cc_close(*context, ccache); 75153541Sshin if (reply != NULL) 752157366Srwatson krb5_free_ap_rep_enc_part(*context, reply); 753157374Srwatson if (ap.length > 0) 75453541Sshin krb5_data_free(&ap); 75553541Sshin 75653541Sshin return ret; 75783366Sjulian 75853541Sshin} 759180305Srwatson 76053541Sshinvoid 761194760Srwatsonsend_krb5_tgt(krb5_context context, krb5_auth_context auth_context) 762148385Sume{ 76353541Sshin int fd; 764180305Srwatson int type, payload_len; 765157374Srwatson krb5_error_code problem; 766180305Srwatson krb5_data outbuf; 76753541Sshin krb5_ccache ccache = NULL; 768180305Srwatson krb5_creds creds; 769188144Sjamie krb5_kdc_flags flags; 770188144Sjamie const char* remotehost = get_canonical_hostname(); 771181803Sbz 772180305Srwatson memset(&creds, 0, sizeof(creds)); 773181803Sbz memset(&outbuf, 0, sizeof(outbuf)); 774180305Srwatson 775148385Sume fd = packet_get_connection_in(); 77653541Sshin problem = krb5_auth_con_setaddrs_from_fd(context, auth_context, &fd); 777194760Srwatson if (problem) { 778180305Srwatson goto out; 779194760Srwatson } 780194760Srwatson 78153541Sshin#if 0 78253541Sshin tkfile = krb5_cc_default_name(context); 783194760Srwatson if (strncmp(tkfile, "FILE:", 5) == 0) 784120856Sume tkfile += 5; 78553541Sshin 786194760Srwatson if (stat(tkfile, &buf) == 0 && getuid() != buf.st_uid) { 787194760Srwatson debug("Kerberos V5: could not get default ccache (permission denied)."); 788181803Sbz goto out; 789178285Srwatson } 79053541Sshin#endif 791178285Srwatson 792181803Sbz problem = krb5_cc_default(context, &ccache); 793180305Srwatson if (problem) { 79453541Sshin goto out; 79553541Sshin } 79653541Sshin 79783366Sjulian problem = krb5_cc_get_principal(context, ccache, &creds.client); 79853541Sshin if (problem) { 799180305Srwatson goto out; 80053541Sshin } 801194777Sbz 802148385Sume problem = krb5_build_principal(context, &creds.server, 803148385Sume strlen(creds.client->realm), 80453541Sshin creds.client->realm, 805180305Srwatson "krbtgt", 806157374Srwatson creds.client->realm, 807180305Srwatson NULL); 80853541Sshin if (problem) { 809180305Srwatson goto out; 810181803Sbz } 811180305Srwatson 81253541Sshin creds.times.endtime = 0; 813180305Srwatson 814148385Sume flags.i = 0; 815148385Sume flags.b.forwarded = 1; 816180305Srwatson flags.b.forwardable = krb5_config_get_bool(context, NULL, 817180305Srwatson "libdefaults", "forwardable", NULL); 818180305Srwatson 819180305Srwatson problem = krb5_get_forwarded_creds (context, 820180305Srwatson auth_context, 821180305Srwatson ccache, 822148385Sume flags.i, 823181803Sbz remotehost, 824148385Sume &creds, 825181803Sbz &outbuf); 826180305Srwatson if (problem) { 827148385Sume goto out; 828181803Sbz } 829178285Srwatson 83053541Sshin packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); 831194777Sbz packet_put_string((char *)outbuf.data, outbuf.length); 832194777Sbz packet_send(); 833194777Sbz packet_write_wait(); 834178285Srwatson 835181803Sbz type = packet_read(&payload_len); 836194777Sbz switch (type) { 837132714Srwatson case SSH_SMSG_SUCCESS: 838148385Sume break; 839148385Sume case SSH_SMSG_FAILURE: 840148385Sume break; 841148385Sume default: 842178285Srwatson break; 843181803Sbz } 844180305Srwatson 845148385Sumeout: 846148385Sume if (creds.client) 847194777Sbz krb5_free_principal(context, creds.client); 84853541Sshin if (creds.server) 849178285Srwatson krb5_free_principal(context, creds.server); 850181803Sbz if (ccache) 851180305Srwatson krb5_cc_close(context, ccache); 85253541Sshin if (outbuf.data) 85353541Sshin xfree(outbuf.data); 85453541Sshin 85553541Sshin return; 85653541Sshin} 857132714Srwatson#endif /* KRB5 */ 858132714Srwatson 859132714Srwatson/* 860157374Srwatson * Starts a dialog with the server, and authenticates the current user on the 861180305Srwatson * server. This does not need any extra privileges. The basic connection 862178285Srwatson * to the server must already have been established before this is called. 86353541Sshin * If login fails, this function prints an error and never returns. 864178285Srwatson * This function does not require super-user privileges. 865180305Srwatson */ 86653541Sshinvoid 86753541Sshinssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, 86853541Sshin struct sockaddr *hostaddr, uid_t original_real_uid) 86953541Sshin{ 870171260Sdelphij struct passwd *pw; 87153541Sshin char *host, *cp; 872180305Srwatson char *server_user, *local_user; 87353541Sshin 87453541Sshin /* Get local user name. Use it as server user if no user name was given. */ 875132714Srwatson pw = getpwuid(original_real_uid); 87653541Sshin if (!pw) 877180305Srwatson fatal("User id %u not found from user database.", original_real_uid); 878157374Srwatson local_user = xstrdup(pw->pw_name); 879180305Srwatson server_user = options.user ? options.user : local_user; 880180305Srwatson 881132714Srwatson /* Convert the user-supplied hostname into all lowercase. */ 88253541Sshin host = xstrdup(orighost); 88353541Sshin for (cp = host; *cp; cp++) 88453541Sshin if (isupper(*cp)) 885180305Srwatson *cp = tolower(*cp); 88653541Sshin 88753541Sshin /* Exchange protocol version identification strings with the server. */ 88853541Sshin ssh_exchange_identification(); 88953541Sshin 89053541Sshin /* Put the connection into non-blocking mode. */ 891180990Srwatson packet_set_nonblocking(); 89253541Sshin 893180990Srwatson /* key exchange */ 894180990Srwatson /* authenticate user */ 89553541Sshin if (compat20) { 89653541Sshin ssh_kex2(host, hostaddr); 89753541Sshin ssh_userauth2(server_user, host); 89853541Sshin } else { 899180305Srwatson ssh_kex(host, hostaddr); 90053541Sshin ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key); 901148385Sume } 902148385Sume} 903180305Srwatson