X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/ce26c02ade0b57cc92c9de4794830f9bd8d0ead3..9555d25824c6cc80227cc4c65980503935156cbe:/sshconnect.c?ds=sidebyside diff --git a/sshconnect.c b/sshconnect.c index f29ac808..33961e4d 100644 --- a/sshconnect.c +++ b/sshconnect.c @@ -13,9 +13,15 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshconnect.c,v 1.148 2003/09/18 07:52:54 markus Exp $"); -#include +#include +#include +#include + +#include +#ifdef HAVE_PATHS_H +#include +#endif #include "ssh.h" #include "xmalloc.h" @@ -31,18 +37,12 @@ RCSID("$OpenBSD: sshconnect.c,v 1.148 2003/09/18 07:52:54 markus Exp $"); #include "readconf.h" #include "atomicio.h" #include "misc.h" -#include "readpass.h" - -#ifdef DNS #include "dns.h" -#endif char *client_version_string = NULL; char *server_version_string = NULL; -#ifdef DNS -int verified_host_key_dns = 0; -#endif +static int matching_host_key_dns = 0; /* import */ extern Options options; @@ -56,6 +56,7 @@ extern pid_t proxy_command_pid; #endif static int show_other_keys(const char *, Key *); +static void warn_changed_key(Key *); /* * Connect to the given ssh server using a proxy command. @@ -63,12 +64,11 @@ static int show_other_keys(const char *, Key *); static int ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) { - Buffer command; - const char *cp; - char *command_string; + char *command_string, *tmp; int pin[2], pout[2]; pid_t pid; char strport[NI_MAXSERV]; + size_t len; /* Convert the port number into a string. */ snprintf(strport, sizeof strport, "%hu", port); @@ -77,34 +77,16 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) * Build the final command string in the buffer by making the * appropriate substitutions to the given proxy command. * - * Use "exec" to avoid "sh -c" processes on some platforms + * Use "exec" to avoid "sh -c" processes on some platforms * (e.g. Solaris) */ - buffer_init(&command); - buffer_append(&command, "exec ", 5); - - for (cp = proxy_command; *cp; cp++) { - if (cp[0] == '%' && cp[1] == '%') { - buffer_append(&command, "%", 1); - cp++; - continue; - } - if (cp[0] == '%' && cp[1] == 'h') { - buffer_append(&command, host, strlen(host)); - cp++; - continue; - } - if (cp[0] == '%' && cp[1] == 'p') { - buffer_append(&command, strport, strlen(strport)); - cp++; - continue; - } - buffer_append(&command, cp, 1); - } - buffer_append(&command, "\0", 1); - - /* Get the final command string. */ - command_string = buffer_ptr(&command); + len = strlen(proxy_command) + 6; + tmp = xmalloc(len); + strlcpy(tmp, "exec ", len); + strlcat(tmp, proxy_command, len); + command_string = percent_expand(tmp, "h", host, + "p", strport, (char *)NULL); + xfree(tmp); /* Create pipes for communicating with the proxy. */ if (pipe(pin) < 0 || pipe(pout) < 0) @@ -158,7 +140,7 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) close(pout[1]); /* Free the command name. */ - buffer_free(&command); + xfree(command_string); /* Set the connection file descriptors. */ packet_set_connection(pout[0], pin[1]); @@ -234,12 +216,12 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, if (timeout <= 0) return (connect(sockfd, serv_addr, addrlen)); - if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) - return (-1); - + set_nonblock(sockfd); rc = connect(sockfd, serv_addr, addrlen); - if (rc == 0) + if (rc == 0) { + unset_nonblock(sockfd); return (0); + } if (errno != EINPROGRESS) return (-1); @@ -251,28 +233,28 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, tv.tv_sec = timeout; tv.tv_usec = 0; - for(;;) { + for (;;) { rc = select(sockfd + 1, NULL, fdset, NULL, &tv); if (rc != -1 || errno != EINTR) break; } - switch(rc) { + switch (rc) { case 0: /* Timed out */ errno = ETIMEDOUT; break; case -1: /* Select error */ - debug("select: %s", strerror(errno)); + debug("select: %s", strerror(errno)); break; case 1: /* Completed or failed */ optval = 0; optlen = sizeof(optval); - if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, + if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { - debug("getsockopt: %s", strerror(errno)); + debug("getsockopt: %s", strerror(errno)); break; } if (optval != 0) { @@ -280,6 +262,7 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, break; } result = 0; + unset_nonblock(sockfd); break; default: /* Should not occur */ @@ -300,12 +283,6 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr, * second). If proxy_command is non-NULL, it specifies the command (with %h * and %p substituted for host and port, respectively) to use to contact * the daemon. - * Return values: - * 0 for OK - * ECONNREFUSED if we got a "Connection Refused" by the peer on any address - * ECONNABORTED if we failed without a "Connection refused" - * Suitable error messages for the connection failure will already have been - * printed. */ int ssh_connect(const char *host, struct sockaddr_storage * hostaddr, @@ -317,24 +294,9 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, int sock = -1, attempt; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; struct addrinfo hints, *ai, *aitop; - struct servent *sp; - /* - * Did we get only other errors than "Connection refused" (which - * should block fallback to rsh and similar), or did we get at least - * one "Connection refused"? - */ - int full_failure = 1; debug2("ssh_connect: needpriv %d", needpriv); - /* Get default port if port has not been set. */ - if (port == 0) { - sp = getservbyname(SSH_SERVICE_NAME, "tcp"); - if (sp) - port = ntohs(sp->s_port); - else - port = SSH_DEFAULT_PORT; - } /* If a proxy command is given, connect using it. */ if (proxy_command != NULL) return ssh_proxy_connect(host, port, proxy_command); @@ -384,8 +346,6 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); break; } else { - if (errno == ECONNREFUSED) - full_failure = 0; debug("connect to address %s port %s: %s", ntop, strport, strerror(errno)); /* @@ -411,15 +371,15 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, /* Return failure if we didn't get a successful connection. */ if (attempt >= connection_attempts) { - logit("ssh: connect to host %s port %s: %s", + error("ssh: connect to host %s port %s: %s", host, strport, strerror(errno)); - return full_failure ? ECONNABORTED : ECONNREFUSED; + return (-1); } debug("Connection established."); - /* Set keepalives if requested. */ - if (options.keepalives && + /* Set SO_KEEPALIVE if requested. */ + if (options.tcp_keep_alive && setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)) < 0) error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); @@ -438,19 +398,21 @@ static void ssh_exchange_identification(void) { char buf[256], remote_version[256]; /* must be same size! */ - int remote_major, remote_minor, i, mismatch; + int remote_major, remote_minor, mismatch; int connection_in = packet_get_connection_in(); int connection_out = packet_get_connection_out(); int minor1 = PROTOCOL_MINOR_1; + u_int i; - /* Read other side\'s version identification. */ + /* Read other side's version identification. */ for (;;) { for (i = 0; i < sizeof(buf) - 1; i++) { - int len = atomicio(read, connection_in, &buf[i], 1); - if (len < 0) - fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); - if (len != 1) + size_t len = atomicio(read, connection_in, &buf[i], 1); + + if (len != 1 && errno == EPIPE) fatal("ssh_exchange_identification: Connection closed by remote host"); + else if (len != 1) + fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); if (buf[i] == '\r') { buf[i] = '\n'; buf[i + 1] = 0; @@ -566,12 +528,12 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, int readonly, const char *user_hostfile, const char *system_hostfile) { Key *file_key; - char *type = key_type(host_key); + const char *type = key_type(host_key); char *ip = NULL; char hostline[1000], *hostp, *fp; HostStatus host_status; HostStatus ip_status; - int local = 0, host_ip_differ = 0; + int r, local = 0, host_ip_differ = 0; int salen; char ntop[NI_MAXHOST]; char msg[1024]; @@ -590,7 +552,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, switch (hostaddr->sa_family) { case AF_INET: local = (ntohl(((struct sockaddr_in *)hostaddr)-> - sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; + sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; salen = sizeof(struct sockaddr_in); break; case AF_INET6: @@ -647,7 +609,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, file_key = key_new(host_key->type); /* - * Check if the host key is present in the user\'s list of known + * Check if the host key is present in the user's list of known * hosts or in the systemwide list. */ host_file = user_hostfile; @@ -695,7 +657,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, "'%.128s' not in list of known hosts.", type, ip); else if (!add_host_to_hostfile(user_hostfile, ip, - host_key)) + host_key, options.hash_known_hosts)) logit("Failed to add the %s host key for IP " "address '%.128s' to the list of known " "hosts (%.30s).", type, ip, user_hostfile); @@ -723,16 +685,15 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, if (show_other_keys(host, host_key)) snprintf(msg1, sizeof(msg1), - "\nbut keys of different type are already" - " known for this host."); + "\nbut keys of different type are already" + " known for this host."); else snprintf(msg1, sizeof(msg1), "."); /* The default */ fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX); msg2[0] = '\0'; -#ifdef DNS if (options.verify_host_key_dns) { - if (verified_host_key_dns) + if (matching_host_key_dns) snprintf(msg2, sizeof(msg2), "Matching host key fingerprint" " found in DNS.\n"); @@ -741,7 +702,6 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, "No matching host key fingerprint" " found in DNS.\n"); } -#endif snprintf(msg, sizeof(msg), "The authenticity of host '%.200s (%s)' can't be " "established%s\n" @@ -753,17 +713,33 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, if (!confirm(msg)) goto fail; } - if (options.check_host_ip && ip_status == HOST_NEW) { - snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); - hostp = hostline; - } else - hostp = host; - /* * If not in strict mode, add the key automatically to the * local known_hosts file. */ - if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) + if (options.check_host_ip && ip_status == HOST_NEW) { + snprintf(hostline, sizeof(hostline), "%s,%s", + host, ip); + hostp = hostline; + if (options.hash_known_hosts) { + /* Add hash of host and IP separately */ + r = add_host_to_hostfile(user_hostfile, host, + host_key, options.hash_known_hosts) && + add_host_to_hostfile(user_hostfile, ip, + host_key, options.hash_known_hosts); + } else { + /* Add unhashed "host,ip" */ + r = add_host_to_hostfile(user_hostfile, + hostline, host_key, + options.hash_known_hosts); + } + } else { + r = add_host_to_hostfile(user_hostfile, host, host_key, + options.hash_known_hosts); + hostp = host; + } + + if (!r) logit("Failed to add the host to the list of known " "hosts (%.500s).", user_hostfile); else @@ -772,39 +748,29 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, break; case HOST_CHANGED: if (options.check_host_ip && host_ip_differ) { - char *msg; + char *key_msg; if (ip_status == HOST_NEW) - msg = "is unknown"; + key_msg = "is unknown"; else if (ip_status == HOST_OK) - msg = "is unchanged"; + key_msg = "is unchanged"; else - msg = "has a different value"; + key_msg = "has a different value"; error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("The %s host key for %s has changed,", type, host); error("and the key for the according IP address %s", ip); - error("%s. This could either mean that", msg); + error("%s. This could either mean that", key_msg); error("DNS SPOOFING is happening or the IP address for the host"); 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."); + warn_changed_key(host_key); error("Add correct host key in %.100s to get rid of this message.", 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 @@ -907,27 +873,27 @@ int verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) { struct stat st; + int flags = 0; -#ifdef DNS - if (options.verify_host_key_dns) { - switch(verify_host_key_dns(host, hostaddr, host_key)) { - case DNS_VERIFY_OK: -#ifdef DNSSEC - return 0; -#else - verified_host_key_dns = 1; - break; -#endif - case DNS_VERIFY_FAILED: - return -1; - case DNS_VERIFY_ERROR: - break; - default: - debug3("bad return value from verify_host_key_dns"); - break; + if (options.verify_host_key_dns && + verify_host_key_dns(host, hostaddr, host_key, &flags) == 0) { + + if (flags & DNS_VERIFY_FOUND) { + + if (options.verify_host_key_dns == 1 && + flags & DNS_VERIFY_MATCH && + flags & DNS_VERIFY_SECURE) + return 0; + + if (flags & DNS_VERIFY_MATCH) { + matching_host_key_dns = 1; + } else { + warn_changed_key(host_key); + error("Update the SSHFP RR in DNS with the new " + "host key to get rid of this message."); + } } } -#endif /* DNS */ /* return ok if the key can be found in an old keyfile */ if (stat(options.system_hostfile2, &st) == 0 || @@ -961,7 +927,7 @@ ssh_login(Sensitive *sensitive, const char *orighost, host = xstrdup(orighost); for (cp = host; *cp; cp++) if (isupper(*cp)) - *cp = tolower(*cp); + *cp = (char)tolower(*cp); /* Exchange protocol version identification strings with the server. */ ssh_exchange_identification(); @@ -1053,3 +1019,60 @@ show_other_keys(const char *host, Key *key) } return (found); } + +static void +warn_changed_key(Key *host_key) +{ + char *fp; + const char *type = key_type(host_key); + + 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."); + + xfree(fp); +} + +/* + * Execute a local command + */ +int +ssh_local_cmd(const char *args) +{ + char *shell; + pid_t pid; + int status; + + if (!options.permit_local_command || + args == NULL || !*args) + return (1); + + if ((shell = getenv("SHELL")) == NULL) + shell = _PATH_BSHELL; + + pid = fork(); + if (pid == 0) { + debug3("Executing %s -c \"%s\"", shell, args); + execl(shell, shell, "-c", args, (char *)NULL); + error("Couldn't execute %s -c \"%s\": %s", + shell, args, strerror(errno)); + _exit(1); + } else if (pid == -1) + fatal("fork failed: %.100s", strerror(errno)); + while (waitpid(pid, &status, 0) == -1) + if (errno != EINTR) + fatal("Couldn't wait for child: %s", strerror(errno)); + + if (!WIFEXITED(status)) + return (1); + + return (WEXITSTATUS(status)); +}