]> andersk Git - openssh.git/blobdiff - sshconnect.c
- (tim) [defines.h] Fix regression in long password support on OpenServer 6.
[openssh.git] / sshconnect.c
index d70410a8e6d15d759e19373dc34818d464d5b4b6..933df39fa34a2c42bbe0d3eab7e3e087a7f9644e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect.c,v 1.196 2006/07/26 13:57:17 stevesk Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.202 2007/09/04 11:15:55 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -24,6 +24,7 @@
 #endif
 
 #include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include <ctype.h>
 #include <errno.h>
 #include <paths.h>
 #endif
 #include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-#include "ssh.h"
 #include "xmalloc.h"
+#include "key.h"
+#include "hostfile.h"
+#include "ssh.h"
 #include "rsa.h"
 #include "buffer.h"
 #include "packet.h"
@@ -72,6 +77,23 @@ extern pid_t proxy_command_pid;
 static int show_other_keys(const char *, Key *);
 static void warn_changed_key(Key *);
 
+static void
+ms_subtract_diff(struct timeval *start, int *ms)
+{
+       struct timeval diff, finish;
+
+       gettimeofday(&finish, NULL);
+       timersub(&finish, start, &diff);        
+       *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000);
+}
+
+static void
+ms_to_timeval(struct timeval *tv, int ms)
+{
+       tv->tv_sec = ms / 1000;
+       tv->tv_usec = (ms % 1000) * 1000;
+}
+
 /*
  * Connect to the given ssh server using a proxy command.
  */
@@ -81,7 +103,10 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
        char *command_string, *tmp;
        int pin[2], pout[2];
        pid_t pid;
-       char strport[NI_MAXSERV];
+       char *shell, strport[NI_MAXSERV];
+
+       if ((shell = getenv("SHELL")) == NULL)
+               shell = _PATH_BSHELL;
 
        /* Convert the port number into a string. */
        snprintf(strport, sizeof strport, "%hu", port);
@@ -127,7 +152,7 @@ ssh_proxy_connect(const char *host, u_short port, const char *proxy_command)
 
                /* Stderr is left as it is so that error messages get
                   printed on the user's terminal. */
-               argv[0] = _PATH_BSHELL;
+               argv[0] = shell;
                argv[1] = "-c";
                argv[2] = command_string;
                argv[3] = NULL;
@@ -215,30 +240,36 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
 
 static int
 timeout_connect(int sockfd, const struct sockaddr *serv_addr,
-    socklen_t addrlen, int timeout)
+    socklen_t addrlen, int *timeoutp)
 {
        fd_set *fdset;
-       struct timeval tv;
+       struct timeval tv, t_start;
        socklen_t optlen;
        int optval, rc, result = -1;
 
-       if (timeout <= 0)
-               return (connect(sockfd, serv_addr, addrlen));
+       gettimeofday(&t_start, NULL);
+
+       if (*timeoutp <= 0) {
+               result = connect(sockfd, serv_addr, addrlen);
+               goto done;
+       }
 
        set_nonblock(sockfd);
        rc = connect(sockfd, serv_addr, addrlen);
        if (rc == 0) {
                unset_nonblock(sockfd);
-               return (0);
+               result = 0;
+               goto done;
+       }
+       if (errno != EINPROGRESS) {
+               result = -1;
+               goto done;
        }
-       if (errno != EINPROGRESS)
-               return (-1);
 
        fdset = (fd_set *)xcalloc(howmany(sockfd + 1, NFDBITS),
            sizeof(fd_mask));
        FD_SET(sockfd, fdset);
-       tv.tv_sec = timeout;
-       tv.tv_usec = 0;
+       ms_to_timeval(&tv, *timeoutp);
 
        for (;;) {
                rc = select(sockfd + 1, NULL, fdset, NULL, &tv);
@@ -277,6 +308,16 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr,
        }
 
        xfree(fdset);
+
+ done:
+       if (result == 0 && *timeoutp > 0) {
+               ms_subtract_diff(&t_start, timeoutp);
+               if (*timeoutp <= 0) {
+                       errno = ETIMEDOUT;
+                       result = -1;
+               }
+       }
+
        return (result);
 }
 
@@ -293,8 +334,8 @@ timeout_connect(int sockfd, const struct sockaddr *serv_addr,
  */
 int
 ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
-    u_short port, int family, int connection_attempts,
-    int needpriv, const char *proxy_command)
+    u_short port, int family, int connection_attempts, int *timeout_ms,
+    int want_keepalive, int needpriv, const char *proxy_command)
 {
        int gaierr;
        int on = 1;
@@ -319,9 +360,11 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
                    gai_strerror(gaierr));
 
        for (attempt = 0; attempt < connection_attempts; attempt++) {
-               if (attempt > 0)
+               if (attempt > 0) {
+                       /* Sleep a moment before retrying. */
+                       sleep(1);
                        debug("Trying again...");
-
+               }
                /*
                 * Loop through addresses for this host, and try each one in
                 * sequence until the connection succeeds.
@@ -345,7 +388,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
                                continue;
 
                        if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen,
-                           options.connection_timeout) >= 0) {
+                           timeout_ms) >= 0) {
                                /* Successful connection. */
                                memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
                                break;
@@ -358,9 +401,6 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
                }
                if (sock != -1)
                        break;  /* Successful connection. */
-
-               /* Sleep a moment before retrying. */
-               sleep(1);
        }
 
        freeaddrinfo(aitop);
@@ -375,7 +415,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
        debug("Connection established.");
 
        /* Set SO_KEEPALIVE if requested. */
-       if (options.tcp_keep_alive &&
+       if (want_keepalive &&
            setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
            sizeof(on)) < 0)
                error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
@@ -391,7 +431,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
  * identification string.
  */
 static void
-ssh_exchange_identification(void)
+ssh_exchange_identification(int timeout_ms)
 {
        char buf[256], remote_version[256];     /* must be same size! */
        int remote_major, remote_minor, mismatch;
@@ -399,16 +439,44 @@ ssh_exchange_identification(void)
        int connection_out = packet_get_connection_out();
        int minor1 = PROTOCOL_MINOR_1;
        u_int i, n;
+       size_t len;
+       int fdsetsz, remaining, rc;
+       struct timeval t_start, t_remaining;
+       fd_set *fdset;
+
+       fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask);
+       fdset = xcalloc(1, fdsetsz);
 
        /* Read other side's version identification. */
+       remaining = timeout_ms;
        for (n = 0;;) {
                for (i = 0; i < sizeof(buf) - 1; i++) {
-                       size_t len = atomicio(read, connection_in, &buf[i], 1);
+                       if (timeout_ms > 0) {
+                               gettimeofday(&t_start, NULL);
+                               ms_to_timeval(&t_remaining, remaining);
+                               FD_SET(connection_in, fdset);
+                               rc = select(connection_in + 1, fdset, NULL,
+                                   fdset, &t_remaining);
+                               ms_subtract_diff(&t_start, &remaining);
+                               if (rc == 0 || remaining <= 0)
+                                       fatal("Connection timed out during "
+                                           "banner exchange");
+                               if (rc == -1) {
+                                       if (errno == EINTR)
+                                               continue;
+                                       fatal("ssh_exchange_identification: "
+                                           "select: %s", strerror(errno));
+                               }
+                       }
+
+                       len = atomicio(read, connection_in, &buf[i], 1);
 
                        if (len != 1 && errno == EPIPE)
-                               fatal("ssh_exchange_identification: Connection closed by remote host");
+                               fatal("ssh_exchange_identification: "
+                                   "Connection closed by remote host");
                        else if (len != 1)
-                               fatal("ssh_exchange_identification: read: %.100s", strerror(errno));
+                               fatal("ssh_exchange_identification: "
+                                   "read: %.100s", strerror(errno));
                        if (buf[i] == '\r') {
                                buf[i] = '\n';
                                buf[i + 1] = 0;
@@ -419,7 +487,8 @@ ssh_exchange_identification(void)
                                break;
                        }
                        if (++n > 65536)
-                               fatal("ssh_exchange_identification: No banner received");
+                               fatal("ssh_exchange_identification: "
+                                   "No banner received");
                }
                buf[sizeof(buf) - 1] = 0;
                if (strncmp(buf, "SSH-", 4) == 0)
@@ -427,6 +496,7 @@ ssh_exchange_identification(void)
                debug("ssh_exchange_identification: %s", buf);
        }
        server_version_string = xstrdup(buf);
+       xfree(fdset);
 
        /*
         * Check that the versions match.  In future this might accept
@@ -521,9 +591,13 @@ confirm(const char *prompt)
  * check whether the supplied host key is valid, return -1 if the key
  * is not valid. the user_hostfile will not be updated if 'readonly' is true.
  */
+#define RDRW   0
+#define RDONLY 1
+#define ROQUIET        2
 static int
-check_host_key(char *hostname, struct sockaddr *hostaddr, Key *host_key,
-    int readonly, const char *user_hostfile, const char *system_hostfile)
+check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
+    Key *host_key, int readonly, const char *user_hostfile,
+    const char *system_hostfile)
 {
        Key *file_key;
        const char *type = key_type(host_key);
@@ -578,7 +652,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, Key *host_key,
                if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop),
                    NULL, 0, NI_NUMERICHOST) != 0)
                        fatal("check_host_key: getnameinfo failed");
-               ip = put_host_port(ntop, options.port);
+               ip = put_host_port(ntop, port);
        } else {
                ip = xstrdup("<no hostip for proxy command>");
        }
@@ -600,7 +674,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, Key *host_key,
                host = xstrdup(options.host_key_alias);
                debug("using hostkeyalias: %s", host);
        } else {
-               host = put_host_port(hostname, options.port);
+               host = put_host_port(hostname, port);
        }
 
        /*
@@ -669,6 +743,15 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, Key *host_key,
                }
                break;
        case HOST_NEW:
+               if (options.host_key_alias == NULL && port != 0 &&
+                   port != SSH_DEFAULT_PORT) {
+                       debug("checking without port identifier");
+                       if (check_host_key(hostname, hostaddr, 0, host_key, 2,
+                           user_hostfile, system_hostfile) == 0) {
+                               debug("found matching key w/out port");
+                               break;
+                       }
+               }
                if (readonly)
                        goto fail;
                /* The host is new. */
@@ -748,6 +831,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, Key *host_key,
                            "list of known hosts.", hostp, type);
                break;
        case HOST_CHANGED:
+               if (readonly == ROQUIET)
+                       goto fail;
                if (options.check_host_ip && host_ip_differ) {
                        char *key_msg;
                        if (ip_status == HOST_NEW)
@@ -906,12 +991,13 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
        /* return ok if the key can be found in an old keyfile */
        if (stat(options.system_hostfile2, &st) == 0 ||
            stat(options.user_hostfile2, &st) == 0) {
-               if (check_host_key(host, hostaddr, host_key, /*readonly*/ 1,
-                   options.user_hostfile2, options.system_hostfile2) == 0)
+               if (check_host_key(host, hostaddr, options.port, host_key,
+                   RDONLY, options.user_hostfile2,
+                   options.system_hostfile2) == 0)
                        return 0;
        }
-       return check_host_key(host, hostaddr, host_key, /*readonly*/ 0,
-           options.user_hostfile, options.system_hostfile);
+       return check_host_key(host, hostaddr, options.port, host_key,
+           RDRW, options.user_hostfile, options.system_hostfile);
 }
 
 /*
@@ -923,7 +1009,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
  */
 void
 ssh_login(Sensitive *sensitive, const char *orighost,
-    struct sockaddr *hostaddr, struct passwd *pw)
+    struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms)
 {
        char *host, *cp;
        char *server_user, *local_user;
@@ -938,7 +1024,7 @@ ssh_login(Sensitive *sensitive, const char *orighost,
                        *cp = (char)tolower(*cp);
 
        /* Exchange protocol version identification strings with the server. */
-       ssh_exchange_identification();
+       ssh_exchange_identification(timeout_ms);
 
        /* Put the connection into non-blocking mode. */
        packet_set_nonblocking();
This page took 0.055009 seconds and 4 git commands to generate.