X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/38c295d646559d5e684af97dbdfe3378871a9081..8002af617f063c404ae06f0b93eb36557ae3d155:/sshconnect.c diff --git a/sshconnect.c b/sshconnect.c index 835ca7b3..60b16a24 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -2,45 +2,50 @@ * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved - * Created: Sat Mar 18 22:15:47 1995 ylo * Code to connect to a remote host, and to perform the client side of the * login (authentication) dialog. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" -RCSID("$OpenBSD: sshconnect.c,v 1.76 2000/06/17 20:30:10 markus Exp $"); +RCSID("$OpenBSD: sshconnect.c,v 1.104 2001/04/12 19:15:25 markus Exp $"); #include -#include -#include +#include "ssh.h" #include "xmalloc.h" #include "rsa.h" -#include "ssh.h" #include "buffer.h" #include "packet.h" #include "uidswap.h" #include "compat.h" -#include "readconf.h" #include "key.h" #include "sshconnect.h" #include "hostfile.h" +#include "log.h" +#include "readconf.h" +#include "atomicio.h" +#include "misc.h" char *client_version_string = NULL; char *server_version_string = NULL; extern Options options; -#ifdef HAVE___PROGNAME extern char *__progname; -#else /* HAVE___PROGNAME */ -static const char *__progname = "ssh"; -#endif /* HAVE___PROGNAME */ + +/* AF_UNSPEC or AF_INET or AF_INET6 */ +extern int IPv4or6; /* * Connect to the given ssh server using a proxy command. */ int -ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, +ssh_proxy_connect(const char *host, u_short port, struct passwd *pw, const char *proxy_command) { Buffer command; @@ -91,7 +96,7 @@ ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, char *argv[10]; /* Child. Permanently give up superuser privileges. */ - permanently_set_uid(original_real_uid); + permanently_set_uid(pw); /* Redirect stdin and stdout. */ close(pin[1]); @@ -108,15 +113,15 @@ ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, /* Stderr is left as it is so that error messages get printed on the user's terminal. */ - argv[0] = "/bin/sh"; + argv[0] = _PATH_BSHELL; argv[1] = "-c"; argv[2] = command_string; argv[3] = NULL; /* Execute the proxy command. Note that we gave up any extra privileges above. */ - execv("/bin/sh", argv); - perror("/bin/sh"); + execv(argv[0], argv); + perror(argv[0]); exit(1); } /* Parent. */ @@ -140,7 +145,7 @@ ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid, * Creates a (possibly privileged) socket for use as the ssh connection. */ int -ssh_create_socket(uid_t original_real_uid, int privileged, int family) +ssh_create_socket(struct passwd *pw, int privileged, int family) { int sock; @@ -160,7 +165,7 @@ ssh_create_socket(uid_t original_real_uid, int privileged, int family) * Just create an ordinary socket on arbitrary port. We use * the user's uid to create the socket. */ - temporarily_use_uid(original_real_uid); + temporarily_use_uid(pw); sock = socket(family, SOCK_STREAM, 0); if (sock < 0) error("socket: %.100s", strerror(errno)); @@ -183,18 +188,19 @@ ssh_create_socket(uid_t original_real_uid, int privileged, int family) int ssh_connect(const char *host, struct sockaddr_storage * hostaddr, u_short port, int connection_attempts, - int anonymous, uid_t original_real_uid, + int anonymous, struct passwd *pw, const char *proxy_command) { + int gaierr; + int on = 1; int sock = -1, attempt; - struct servent *sp; - struct addrinfo hints, *ai, *aitop; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; - int gaierr; + struct addrinfo hints, *ai, *aitop; struct linger linger; + struct servent *sp; - debug("ssh_connect: getuid %d geteuid %d anon %d", - (int) getuid(), (int) geteuid(), anonymous); + debug("ssh_connect: getuid %u geteuid %u anon %d", + (u_int) getuid(), (u_int) geteuid(), anonymous); /* Get default port if port has not been set. */ if (port == 0) { @@ -206,7 +212,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, } /* If a proxy command is given, connect using it. */ if (proxy_command != NULL) - return ssh_proxy_connect(host, port, original_real_uid, proxy_command); + return ssh_proxy_connect(host, port, pw, proxy_command); /* No proxy command. */ @@ -242,8 +248,12 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, host, ntop, strport); /* Create a socket for connecting. */ - sock = ssh_create_socket(original_real_uid, - !anonymous && geteuid() == 0 && port < IPPORT_RESERVED, + sock = ssh_create_socket(pw, +#ifdef HAVE_CYGWIN + !anonymous, +#else + !anonymous && geteuid() == 0, +#endif ai->ai_family); if (sock < 0) continue; @@ -252,7 +262,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, * hope that it will help with tcp_wrappers showing * the remote uid as root. */ - temporarily_use_uid(original_real_uid); + temporarily_use_uid(pw); if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) { /* Successful connection. */ memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); @@ -293,7 +303,13 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ linger.l_onoff = 1; linger.l_linger = 5; - setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); + setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); + + /* Set keepalives if requested. */ + if (options.keepalives && + setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, + sizeof(on)) < 0) + error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); /* Set the connection. */ packet_set_connection(sock, sock); @@ -306,12 +322,13 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, * identification string. */ void -ssh_exchange_identification() +ssh_exchange_identification(void) { char buf[256], remote_version[256]; /* must be same size! */ int remote_major, remote_minor, i, mismatch; int connection_in = packet_get_connection_in(); int connection_out = packet_get_connection_out(); + int minor1 = PROTOCOL_MINOR_1; /* Read other side\'s version identification. */ for (;;) { @@ -365,9 +382,10 @@ ssh_exchange_identification() } if (remote_minor < 3) { fatal("Remote machine has too old SSH software version."); - } else if (remote_minor == 3) { + } else if (remote_minor == 3 || remote_minor == 4) { /* We speak 1.3, too. */ enable_compat13(); + minor1 = 3; if (options.forward_agent) { log("Agent forwarding disabled for protocol 1.3"); options.forward_agent = 0; @@ -393,7 +411,7 @@ ssh_exchange_identification() /* Send our own protocol version identification. */ snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, - compat20 ? PROTOCOL_MINOR_2 : PROTOCOL_MINOR_1, + compat20 ? PROTOCOL_MINOR_2 : minor1, SSH_VERSION); if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf)) fatal("write: %.100s", strerror(errno)); @@ -403,6 +421,7 @@ ssh_exchange_identification() debug("Local version string %.100s", client_version_string); } +/* defaults to 'no' */ int read_yes_or_no(const char *prompt, int defval) { @@ -410,10 +429,13 @@ read_yes_or_no(const char *prompt, int defval) FILE *f; int retval = -1; - if (isatty(0)) + if (options.batch_mode) + return 0; + + if (isatty(STDIN_FILENO)) f = stdin; else - f = fopen("/dev/tty", "rw"); + f = fopen(_PATH_TTY, "rw"); if (f == NULL) return 0; @@ -435,8 +457,10 @@ read_yes_or_no(const char *prompt, int defval) retval = defval; if (strcmp(buf, "yes") == 0) retval = 1; - if (strcmp(buf, "no") == 0) + else if (strcmp(buf, "no") == 0) retval = 0; + else + fprintf(stderr, "Please type 'yes' or 'no'.\n"); if (retval != -1) { if (f != stdin) @@ -457,12 +481,14 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, Key *file_key; char *type = key_type(host_key); char *ip = NULL; - char hostline[1000], *hostp; + char hostline[1000], *hostp, *fp; HostStatus host_status; HostStatus ip_status; int local = 0, host_ip_differ = 0; int salen; char ntop[NI_MAXHOST]; + int host_line, ip_line; + const char *host_file = NULL, *ip_file = NULL; /* * Force accepting of the host key for loopback/localhost. The @@ -487,23 +513,40 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, salen = sizeof(struct sockaddr_storage); break; } - if (local) { - debug("Forcing accepting of host key for loopback/localhost."); + if (local && options.host_key_alias == NULL) { + debug("Forcing accepting of host key for " + "loopback/localhost."); return; } /* - * Turn off check_host_ip for proxy connects, since - * we don't have the remote ip-address + * We don't have the remote ip-address for connections + * using a proxy command */ - if (options.proxy_command != NULL && options.check_host_ip) - options.check_host_ip = 0; - - if (options.check_host_ip) { + if (options.proxy_command == NULL) { if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) fatal("check_host_key: getnameinfo failed"); ip = xstrdup(ntop); + } else { + ip = xstrdup(""); + } + /* + * Turn off check_host_ip if the connection is to localhost, via proxy + * command or if we don't have a hostname to compare with + */ + if (options.check_host_ip && + (local || strcmp(host, ip) == 0 || options.proxy_command != NULL)) + options.check_host_ip = 0; + + /* + * Allow the user to record the key under a different name. This is + * useful for ssh tunneling over forwarded connections or if you run + * multiple sshd's on different ports on the same machine. + */ + if (options.host_key_alias != NULL) { + host = options.host_key_alias; + debug("using hostkeyalias: %s", host); } /* @@ -516,19 +559,25 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, * Check if the host key is present in the user\'s list of known * hosts or in the systemwide list. */ - host_status = check_host_in_hostfile(user_hostfile, host, host_key, file_key); - if (host_status == HOST_NEW) - host_status = check_host_in_hostfile(system_hostfile, host, host_key, file_key); + host_file = user_hostfile; + host_status = check_host_in_hostfile(host_file, host, host_key, file_key, &host_line); + if (host_status == HOST_NEW) { + host_file = system_hostfile; + host_status = check_host_in_hostfile(host_file, host, host_key, file_key, &host_line); + } /* * Also perform check for the ip address, skip the check if we are * localhost or the hostname was an ip address to begin with */ - if (options.check_host_ip && !local && strcmp(host, ip)) { + if (options.check_host_ip) { Key *ip_key = key_new(host_key->type); - ip_status = check_host_in_hostfile(user_hostfile, ip, host_key, ip_key); - if (ip_status == HOST_NEW) - ip_status = check_host_in_hostfile(system_hostfile, ip, host_key, ip_key); + ip_file = user_hostfile; + ip_status = check_host_in_hostfile(ip_file, ip, host_key, ip_key, &ip_line); + if (ip_status == HOST_NEW) { + ip_file = system_hostfile; + ip_status = check_host_in_hostfile(ip_file, ip, host_key, ip_key, &ip_line); + } if (host_status == HOST_CHANGED && (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) host_ip_differ = 1; @@ -544,17 +593,14 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, /* The host is known and the key matches. */ debug("Host '%.200s' is known and matches the %s host key.", host, type); - if (options.check_host_ip) { - if (ip_status == HOST_NEW) { - if (!add_host_to_hostfile(user_hostfile, ip, host_key)) - log("Failed to add the %s host key for IP address '%.30s' to the list of known hosts (%.30s).", - type, ip, user_hostfile); - else - log("Warning: Permanently added the %s host key for IP address '%.30s' to the list of known hosts.", - type, ip); - } else if (ip_status != HOST_OK) - log("Warning: the %s host key for '%.200s' differs from the key for the IP address '%.30s'", - type, host, ip); + debug("Found key in %s:%d", host_file, host_line); + if (options.check_host_ip && ip_status == HOST_NEW) { + if (!add_host_to_hostfile(user_hostfile, ip, host_key)) + log("Failed to add the %s host key for IP address '%.128s' to the list of known hosts (%.30s).", + type, ip, user_hostfile); + else + log("Warning: Permanently added the %s host key for IP address '%.128s' to the list of known hosts.", + type, ip); } break; case HOST_NEW: @@ -566,16 +612,17 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, } else if (options.strict_host_key_checking == 2) { /* The default */ char prompt[1024]; - char *fp = key_fingerprint(host_key); + fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); snprintf(prompt, sizeof(prompt), - "The authenticity of host '%.200s' can't be established.\n" + "The authenticity of host '%.200s (%s)' can't be established.\n" "%s key fingerprint is %s.\n" "Are you sure you want to continue connecting (yes/no)? ", - host, type, fp); + host, ip, type, fp); + xfree(fp); if (!read_yes_or_no(prompt, -1)) - fatal("Aborted by user!\n"); + fatal("Aborted by user!"); } - if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) { + if (options.check_host_ip && ip_status == HOST_NEW) { snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); hostp = hostline; } else @@ -605,18 +652,25 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, error("and the key for the according IP address %s", ip); error("%s. This could either mean that", msg); error("DNS SPOOFING is happening or the IP address for the host"); - error("and its host key have changed at the same time"); + error("and its host key have changed at the same time."); + if (ip_status != HOST_NEW) + error("Offending key for IP in %s:%d", ip_file, ip_line); } /* The host key has changed. */ + fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); error("It is also possible that the %s host key has just been changed.", type); + error("The fingerprint for the %s key sent by the remote host is\n%s.", + type, fp); error("Please contact your system administrator."); error("Add correct host key in %.100s to get rid of this message.", - user_hostfile); + user_hostfile); + error("Offending key in %s:%d", host_file, host_line); + xfree(fp); /* * If strict host key checking is in use, the user will have @@ -638,6 +692,14 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, error("Agent forwarding is disabled to avoid trojan horses."); options.forward_agent = 0; } + if (options.forward_x11) { + error("X11 forwarding is disabled to avoid trojan horses."); + options.forward_x11 = 0; + } + if (options.num_local_forwards > 0 || options.num_remote_forwards > 0) { + error("Port forwarding is disabled to avoid trojan horses."); + options.num_local_forwards = options.num_remote_forwards = 0; + } /* * XXX Should permit the user to change to use the new id. * This could be done by converting the host key to an @@ -647,8 +709,25 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, */ break; } - if (options.check_host_ip) - xfree(ip); + + if (options.check_host_ip && host_status != HOST_CHANGED && + ip_status == HOST_CHANGED) { + log("Warning: the %s host key for '%.200s' " + "differs from the key for the IP address '%.128s'", + type, host, ip); + if (host_status == HOST_OK) + log("Matching host key in %s:%d", host_file, host_line); + log("Offending key for IP in %s:%d", ip_file, ip_line); + if (options.strict_host_key_checking == 1) { + fatal("Exiting, you have requested strict checking."); + } else if (options.strict_host_key_checking == 2) { + if (!read_yes_or_no("Are you sure you want " \ + "to continue connecting (yes/no)? ", -1)) + fatal("Aborted by user!"); + } + } + + xfree(ip); } /* @@ -659,17 +738,12 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, * This function does not require super-user privileges. */ void -ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, - struct sockaddr *hostaddr, uid_t original_real_uid) +ssh_login(Key **keys, int nkeys, const char *orighost, + struct sockaddr *hostaddr, struct passwd *pw) { - struct passwd *pw; char *host, *cp; char *server_user, *local_user; - /* Get local user name. Use it as server user if no user name was given. */ - pw = getpwuid(original_real_uid); - if (!pw) - fatal("User id %d not found from user database.", original_real_uid); local_user = xstrdup(pw->pw_name); server_user = options.user ? options.user : local_user; @@ -689,9 +763,28 @@ ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, /* authenticate user */ if (compat20) { ssh_kex2(host, hostaddr); - ssh_userauth2(server_user, host); + ssh_userauth2(local_user, server_user, host, keys, nkeys); } else { ssh_kex(host, hostaddr); - ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key); + ssh_userauth1(local_user, server_user, host, keys, nkeys); + } +} + +void +ssh_put_password(char *password) +{ + int size; + char *padded; + + if (datafellows & SSH_BUG_PASSWORDPAD) { + packet_put_string(password, strlen(password)); + return; } + size = roundup(strlen(password) + 1, 32); + padded = xmalloc(size); + memset(padded, 0, size); + strlcpy(padded, password, size); + packet_put_string(padded, size); + memset(padded, 0, size); + xfree(padded); }