]> andersk Git - openssh.git/blobdiff - sshconnect.c
- Compile fix from Darren_Hall@progressive.com
[openssh.git] / sshconnect.c
index 9cd33c1c16df3388339a6695dc4ef0b74f8b771d..56cc93f2838c81b4a1dd5ac87193ea39de5c8544 100644 (file)
@@ -34,11 +34,14 @@ RCSID("$Id$");
 /* Session id for the current session. */
 unsigned char session_id[16];
 
+extern Options options;
+extern char *__progname;
+
 /*
  * Connect to the given ssh server using a proxy command.
  */
 int
-ssh_proxy_connect(const char *host, int port, uid_t original_real_uid,
+ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid,
                  const char *proxy_command)
 {
        Buffer command;
@@ -46,10 +49,10 @@ ssh_proxy_connect(const char *host, int port, uid_t original_real_uid,
        char *command_string;
        int pin[2], pout[2];
        int pid;
-       char portstring[100];
+       char strport[NI_MAXSERV];
 
        /* Convert the port number into a string. */
-       snprintf(portstring, sizeof portstring, "%d", port);
+       snprintf(strport, sizeof strport, "%hu", port);
 
        /* Build the final command string in the buffer by making the
           appropriate substitutions to the given proxy command. */
@@ -66,7 +69,7 @@ ssh_proxy_connect(const char *host, int port, uid_t original_real_uid,
                        continue;
                }
                if (cp[0] == '%' && cp[1] == 'p') {
-                       buffer_append(&command, portstring, strlen(portstring));
+                       buffer_append(&command, strport, strlen(strport));
                        cp++;
                        continue;
                }
@@ -138,7 +141,7 @@ ssh_proxy_connect(const char *host, int 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)
+ssh_create_socket(uid_t original_real_uid, int privileged, int family)
 {
        int sock;
 
@@ -148,26 +151,28 @@ ssh_create_socket(uid_t original_real_uid, int privileged)
         */
        if (privileged) {
                int p = IPPORT_RESERVED - 1;
-
-               sock = rresvport(&p);
+               sock = rresvport_af(&p, family);
                if (sock < 0)
-                       fatal("rresvport: %.100s", strerror(errno));
+                       fatal("rresvport: af=%d %.100s", family, strerror(errno));
                debug("Allocated local port %d.", p);
        } else {
-               /* Just create an ordinary socket on arbitrary port.  We
-                  use the user's uid to create the socket. */
+               /*
+                * Just create an ordinary socket on arbitrary port.  We use
+                * the user's uid to create the socket.
+                */
                temporarily_use_uid(original_real_uid);
-               sock = socket(AF_INET, SOCK_STREAM, 0);
+               sock = socket(family, SOCK_STREAM, 0);
                if (sock < 0)
-                       fatal("socket: %.100s", strerror(errno));
+                       error("socket: %.100s", strerror(errno));
                restore_uid();
        }
        return sock;
 }
 
 /*
- * Opens a TCP/IP connection to the remote server on the given host.  If
- * port is 0, the default port will be used.  If anonymous is zero,
+ * Opens a TCP/IP connection to the remote server on the given host.
+ * The address of the remote host will be returned in hostaddr.
+ * If port is 0, the default port will be used.  If anonymous is zero,
  * a privileged port will be allocated to make the connection.
  * This requires super-user privileges if anonymous is false.
  * Connection_attempts specifies the maximum number of tries (one per
@@ -176,15 +181,16 @@ ssh_create_socket(uid_t original_real_uid, int privileged)
  * the daemon.
  */
 int
-ssh_connect(const char *host, struct sockaddr_in * hostaddr,
-           int port, int connection_attempts,
+ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
+           u_short port, int connection_attempts,
            int anonymous, uid_t original_real_uid,
            const char *proxy_command)
 {
-       int sock = -1, attempt, i;
-       int on = 1;
+       int sock = -1, attempt;
        struct servent *sp;
-       struct hostent *hp;
+       struct addrinfo hints, *ai, *aitop;
+       char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+       int gaierr;
        struct linger linger;
 
        debug("ssh_connect: getuid %d geteuid %d anon %d",
@@ -204,92 +210,57 @@ ssh_connect(const char *host, struct sockaddr_in * hostaddr,
 
        /* No proxy command. */
 
-       /* No host lookup made yet. */
-       hp = NULL;
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = IPv4or6;
+       hints.ai_socktype = SOCK_STREAM;
+       snprintf(strport, sizeof strport, "%d", port);
+       if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
+               fatal("%s: %.100s: %s", __progname, host,
+                   gai_strerror(gaierr));
 
-       /* Try to connect several times.  On some machines, the first time
-          will sometimes fail.  In general socket code appears to behave
-          quite magically on many machines. */
+       /*
+        * Try to connect several times.  On some machines, the first time
+        * will sometimes fail.  In general socket code appears to behave
+        * quite magically on many machines.
+        */
        for (attempt = 0; attempt < connection_attempts; attempt++) {
                if (attempt > 0)
                        debug("Trying again...");
 
-               /* Try to parse the host name as a numeric inet address. */
-               memset(hostaddr, 0, sizeof(hostaddr));
-               hostaddr->sin_family = AF_INET;
-               hostaddr->sin_port = htons(port);
-               hostaddr->sin_addr.s_addr = inet_addr(host);
-               if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff) {
-                       /* Valid numeric IP address */
-                       debug("Connecting to %.100s port %d.",
-                             inet_ntoa(hostaddr->sin_addr), port);
-
-                       /* Create a socket. */
-                       sock = ssh_create_socket(original_real_uid,
-                                         !anonymous && geteuid() == 0 &&
-                                                port < IPPORT_RESERVED);
-
-                       /*
-                        * Connect to the host.  We use the user's uid in the
-                        * hope that it will help with the problems of
-                        * tcp_wrappers showing the remote uid as root.
+               /* Loop through addresses for this host, and try each one in
+                  sequence until the connection succeeds. */
+               for (ai = aitop; ai; ai = ai->ai_next) {
+                       if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+                               continue;
+                       if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
+                           ntop, sizeof(ntop), strport, sizeof(strport),
+                           NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
+                               error("ssh_connect: getnameinfo failed");
+                               continue;
+                       }
+                       debug("Connecting to %.200s [%.100s] port %s.",
+                               host, ntop, strport);
+
+                       /* Create a socket for connecting. */
+                       sock = ssh_create_socket(original_real_uid, 
+                           !anonymous && geteuid() == 0 && port < IPPORT_RESERVED,
+                           ai->ai_family);
+                       if (sock < 0)
+                               continue;
+
+                       /* Connect to the host.  We use the user's uid in the
+                        * hope that it will help with tcp_wrappers showing
+                        * the remote uid as root.
                         */
                        temporarily_use_uid(original_real_uid);
-                       if (connect(sock, (struct sockaddr *) hostaddr, sizeof(*hostaddr))
-                           >= 0) {
-                               /* Successful connect. */
+                       if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) {
+                               /* Successful connection. */
+                               memcpy(hostaddr, ai->ai_addr, sizeof(*(ai->ai_addr)));
                                restore_uid();
                                break;
-                       }
-                       debug("connect: %.100s", strerror(errno));
-                       restore_uid();
-
-                       /* Destroy the failed socket. */
-                       shutdown(sock, SHUT_RDWR);
-                       close(sock);
-               } else {
-                       /* Not a valid numeric inet address. */
-                       /* Map host name to an address. */
-                       if (!hp)
-                               hp = gethostbyname(host);
-                       if (!hp)
-                               fatal("Bad host name: %.100s", host);
-                       if (!hp->h_addr_list[0])
-                               fatal("Host does not have an IP address: %.100s", host);
-
-                       /* Loop through addresses for this host, and try
-                          each one in sequence until the connection
-                          succeeds. */
-                       for (i = 0; hp->h_addr_list[i]; i++) {
-                               /* Set the address to connect to. */
-                               hostaddr->sin_family = hp->h_addrtype;
-                               memcpy(&hostaddr->sin_addr, hp->h_addr_list[i],
-                                      sizeof(hostaddr->sin_addr));
-
-                               debug("Connecting to %.200s [%.100s] port %d.",
-                                     host, inet_ntoa(hostaddr->sin_addr), port);
-
-                               /* Create a socket for connecting. */
-                               sock = ssh_create_socket(original_real_uid,
-                                         !anonymous && geteuid() == 0 &&
-                                                port < IPPORT_RESERVED);
-
-                               /*
-                                * Connect to the host.  We use the user's
-                                * uid in the hope that it will help with
-                                * tcp_wrappers showing the remote uid as
-                                * root.
-                                */
-                               temporarily_use_uid(original_real_uid);
-                               if (connect(sock, (struct sockaddr *) hostaddr,
-                                           sizeof(*hostaddr)) >= 0) {
-                                       /* Successful connection. */
-                                       restore_uid();
-                                       break;
-                               }
+                       } else {
                                debug("connect: %.100s", strerror(errno));
                                restore_uid();
-
                                /*
                                 * Close the failed socket; there appear to
                                 * be some problems when reusing a socket for
@@ -299,13 +270,16 @@ ssh_connect(const char *host, struct sockaddr_in * hostaddr,
                                shutdown(sock, SHUT_RDWR);
                                close(sock);
                        }
-                       if (hp->h_addr_list[i])
-                               break;  /* Successful connection. */
                }
+               if (ai)
+                       break;  /* Successful connection. */
 
                /* Sleep a moment before retrying. */
                sleep(1);
        }
+
+       freeaddrinfo(aitop);
+
        /* Return failure if we didn't get a successful connection. */
        if (attempt >= connection_attempts)
                return 0;
@@ -317,7 +291,6 @@ ssh_connect(const char *host, struct sockaddr_in * hostaddr,
         * as it has been closed for whatever reason.
         */
        /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */
-       setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *) &on, sizeof(on));
        linger.l_onoff = 1;
        linger.l_linger = 5;
        setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger));
@@ -476,9 +449,8 @@ respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv)
  * the user using it.
  */
 int
-try_rsa_authentication(struct passwd * pw, const char *authfile)
+try_rsa_authentication(const char *authfile)
 {
-       extern Options options;
        BIGNUM *challenge;
        RSA *private_key;
        RSA *public_key;
@@ -490,7 +462,8 @@ try_rsa_authentication(struct passwd * pw, const char *authfile)
        public_key = RSA_new();
        if (!load_public_key(authfile, public_key, &comment)) {
                RSA_free(public_key);
-               return 0;       /* Could not load it.  Fail. */
+               /* Could not load it.  Fail. */
+               return 0;
        }
        debug("Trying RSA authentication with key '%.100s'", comment);
 
@@ -513,8 +486,7 @@ try_rsa_authentication(struct passwd * pw, const char *authfile)
        if (type == SSH_SMSG_FAILURE) {
                debug("Server refused our key.");
                xfree(comment);
-               return 0;       /* Server refuses to authenticate with
-                                  this key. */
+               return 0;
        }
        /* Otherwise, the server should respond with a challenge. */
        if (type != SSH_SMSG_AUTH_RSA_CHALLENGE)
@@ -536,7 +508,7 @@ try_rsa_authentication(struct passwd * pw, const char *authfile)
        if (!load_private_key(authfile, "", private_key, NULL)) {
                char buf[300];
                snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ",
-                        comment);
+                   comment);
                if (!options.batch_mode)
                        passphrase = read_passphrase(buf, 0);
                else {
@@ -884,6 +856,93 @@ send_afs_tokens(void)
 
 #endif /* AFS */
 
+/*
+ * Tries to authenticate with any string-based challenge/response system.
+ * Note that the client code is not tied to s/key or TIS.
+ */
+int
+try_skey_authentication()
+{
+       int type, i, payload_len;
+       char *challenge, *response;
+
+       debug("Doing skey authentication.");
+
+       /* request a challenge */
+       packet_start(SSH_CMSG_AUTH_TIS);
+       packet_send();
+       packet_write_wait();
+
+       type = packet_read(&payload_len);
+       if (type != SSH_SMSG_FAILURE &&
+           type != SSH_SMSG_AUTH_TIS_CHALLENGE) {
+               packet_disconnect("Protocol error: got %d in response "
+                                 "to skey-auth", type);
+       }
+       if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) {
+               debug("No challenge for skey authentication.");
+               return 0;
+       }
+       challenge = packet_get_string(&payload_len);
+       if (options.cipher == SSH_CIPHER_NONE)
+               log("WARNING: Encryption is disabled! "
+                   "Reponse will be transmitted in clear text.");
+       fprintf(stderr, "%s\n", challenge);
+       fflush(stderr);
+       for (i = 0; i < options.number_of_password_prompts; i++) {
+               if (i != 0)
+                       error("Permission denied, please try again.");
+               response = read_passphrase("Response: ", 0);
+               packet_start(SSH_CMSG_AUTH_TIS_RESPONSE);
+               packet_put_string(response, strlen(response));
+               memset(response, 0, strlen(response));
+               xfree(response);
+               packet_send();
+               packet_write_wait();
+               type = packet_read(&payload_len);
+               if (type == SSH_SMSG_SUCCESS)
+                       return 1;
+               if (type != SSH_SMSG_FAILURE)
+                       packet_disconnect("Protocol error: got %d in response "
+                                         "to skey-auth-reponse", type);
+       }
+       /* failure */
+       return 0;
+}
+
+/*
+ * Tries to authenticate with plain passwd authentication.
+ */
+int
+try_password_authentication(char *prompt)
+{
+       int type, i, payload_len;
+       char *password;
+
+       debug("Doing password authentication.");
+       if (options.cipher == SSH_CIPHER_NONE)
+               log("WARNING: Encryption is disabled! Password will be transmitted in clear text.");
+       for (i = 0; i < options.number_of_password_prompts; i++) {
+               if (i != 0)
+                       error("Permission denied, please try again.");
+               password = read_passphrase(prompt, 0);
+               packet_start(SSH_CMSG_AUTH_PASSWORD);
+               packet_put_string(password, strlen(password));
+               memset(password, 0, strlen(password));
+               xfree(password);
+               packet_send();
+               packet_write_wait();
+
+               type = packet_read(&payload_len);
+               if (type == SSH_SMSG_SUCCESS)
+                       return 1;
+               if (type != SSH_SMSG_FAILURE)
+                       packet_disconnect("Protocol error: got %d in response to passwd auth", type);
+       }
+       /* failure */
+       return 0;
+}
+
 /*
  * Waits for the server identification string, and sends our own
  * identification string.
@@ -895,7 +954,6 @@ ssh_exchange_identification()
        int remote_major, remote_minor, i;
        int connection_in = packet_get_connection_in();
        int connection_out = packet_get_connection_out();
-       extern Options options;
 
        /* Read other side\'s version identification. */
        for (i = 0; i < sizeof(buf) - 1; i++) {
@@ -930,7 +988,7 @@ ssh_exchange_identification()
        /* We speak 1.3, too. */
        if (remote_major == 1 && remote_minor == 3) {
                enable_compat13();
-               if (options.forward_agent && strcmp(remote_version, SSH_VERSION) != 0) {
+               if (options.forward_agent && strcmp(remote_version, "OpenSSH-1.1") != 0) {
                        log("Agent forwarding disabled, remote version '%s' is not compatible.",
                            remote_version);
                        options.forward_agent = 0;
@@ -949,8 +1007,8 @@ ssh_exchange_identification()
 
        /* Send our own protocol version identification. */
        snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
-                PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
-       if (write(connection_out, buf, strlen(buf)) != strlen(buf))
+           PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
+       if (atomicio(write, connection_out, buf, strlen(buf)) != strlen(buf))
                fatal("write: %.100s", strerror(errno));
 }
 
@@ -1000,127 +1058,70 @@ read_yes_or_no(const char *prompt, int defval)
 }
 
 /*
- * Starts a dialog with the server, and authenticates the current user on the
- * server.  This does not need any extra privileges.  The basic connection
- * to the server must already have been established before this is called.
- * User is the remote user; if it is NULL, the current local user name will
- * be used.  Anonymous indicates that no rhosts authentication will be used.
- * If login fails, this function prints an error and never returns.
- * This function does not require super-user privileges.
+ * check whether the supplied host key is valid, return only if ok.
  */
+
 void
-ssh_login(int host_key_valid,
-         RSA *own_host_key,
-         const char *orighost,
-         struct sockaddr_in *hostaddr,
-         uid_t original_real_uid)
+check_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key)
 {
-       extern Options options;
-       int i, type;
-       char *password;
-       struct passwd *pw;
-       BIGNUM *key;
-       RSA *host_key, *file_key;
-       RSA *public_key;
-       int bits, rbits;
-       unsigned char session_key[SSH_SESSION_KEY_LENGTH];
-       const char *server_user, *local_user;
-       char *cp, *host, *ip = NULL;
+       RSA *file_key;
+       char *ip = NULL;
        char hostline[1000], *hostp;
-       unsigned char check_bytes[8];
-       unsigned int supported_ciphers, supported_authentications, protocol_flags;
        HostStatus host_status;
        HostStatus ip_status;
-       int host_ip_differ = 0;
-       int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
-       int payload_len, clen, sum_len = 0;
-       u_int32_t rand = 0;
-
-       if (options.check_host_ip)
-               ip = xstrdup(inet_ntoa(hostaddr->sin_addr));
-
-       /* Convert the user-supplied hostname into all lowercase. */
-       host = xstrdup(orighost);
-       for (cp = host; *cp; cp++)
-               if (isupper(*cp))
-                       *cp = tolower(*cp);
-
-       /* Exchange protocol version identification strings with the server. */
-       ssh_exchange_identification();
-
-       /* Put the connection into non-blocking mode. */
-       packet_set_nonblocking();
-
-       /* 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;
-
-       debug("Waiting for server public key.");
-
-       /* Wait for a public key packet from the server. */
-       packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY);
-
-       /* Get check bytes from the packet. */
-       for (i = 0; i < 8; i++)
-               check_bytes[i] = packet_get_char();
-
-       /* Get the public key. */
-       public_key = RSA_new();
-       bits = packet_get_int();/* bits */
-       public_key->e = BN_new();
-       packet_get_bignum(public_key->e, &clen);
-       sum_len += clen;
-       public_key->n = BN_new();
-       packet_get_bignum(public_key->n, &clen);
-       sum_len += clen;
+       int local = 0, host_ip_differ = 0;
+       int sa_len;
+       char ntop[NI_MAXHOST];
 
-       rbits = BN_num_bits(public_key->n);
-       if (bits != rbits) {
-               log("Warning: Server lies about size of server public key: "
-                   "actual size is %d bits vs. announced %d.", rbits, bits);
-               log("Warning: This may be due to an old implementation of ssh.");
+       /*
+        * Force accepting of the host key for loopback/localhost. The
+        * problem is that if the home directory is NFS-mounted to multiple
+        * machines, localhost will refer to a different machine in each of
+        * them, and the user will get bogus HOST_CHANGED warnings.  This
+        * essentially disables host authentication for localhost; however,
+        * this is probably not a real problem.
+        */
+       switch (hostaddr->sa_family) {
+       case AF_INET:
+               local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
+               sa_len = sizeof(struct sockaddr_in);
+               break;
+       case AF_INET6:
+               local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
+               sa_len = sizeof(struct sockaddr_in6);
+               break;
+       default:
+               local = 0;
+               sa_len = sizeof(struct sockaddr_storage);
+               break;
+       }
+       if (local) {
+               debug("Forcing accepting of host key for loopback/localhost.");
+               return;
        }
-       /* Get the host key. */
-       host_key = RSA_new();
-       bits = packet_get_int();/* bits */
-       host_key->e = BN_new();
-       packet_get_bignum(host_key->e, &clen);
-       sum_len += clen;
-       host_key->n = BN_new();
-       packet_get_bignum(host_key->n, &clen);
-       sum_len += clen;
 
-       rbits = BN_num_bits(host_key->n);
-       if (bits != rbits) {
-               log("Warning: Server lies about size of server host key: "
-                   "actual size is %d bits vs. announced %d.", rbits, bits);
-               log("Warning: This may be due to an old implementation of ssh.");
+       /*
+        * Turn off check_host_ip for proxy connects, since
+        * we don't have the remote ip-address
+        */
+       if (options.proxy_command != NULL && options.check_host_ip)
+               options.check_host_ip = 0;
+
+       if (options.check_host_ip) {
+               if (getnameinfo(hostaddr, sa_len, ntop, sizeof(ntop),
+                   NULL, 0, NI_NUMERICHOST) != 0)
+                       fatal("check_host_key: getnameinfo failed");
+               ip = xstrdup(ntop);
        }
-       /* Store the host key from the known host file in here so that we
-          can compare it with the key for the IP address. */
+
+       /*
+        * Store the host key from the known host file in here so that we can
+        * compare it with the key for the IP address.
+        */
        file_key = RSA_new();
        file_key->n = BN_new();
        file_key->e = BN_new();
 
-       /* Get protocol flags. */
-       protocol_flags = packet_get_int();
-       packet_set_protocol_flags(protocol_flags);
-
-       supported_ciphers = packet_get_int();
-       supported_authentications = packet_get_int();
-
-       debug("Received server public key (%d bits) and host key (%d bits).",
-             BN_num_bits(public_key->n), BN_num_bits(host_key->n));
-
-       packet_integrity_check(payload_len,
-                              8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4,
-                              SSH_SMSG_PUBLIC_KEY);
-
-       compute_session_id(session_id, check_bytes, host_key->n, public_key->n);
-
        /*
         * Check if the host key is present in the user\'s list of known
         * hosts or in the systemwide list.
@@ -1132,18 +1133,6 @@ ssh_login(int host_key_valid,
                host_status = check_host_in_hostfile(options.system_hostfile, host,
                                                host_key->e, host_key->n,
                                               file_key->e, file_key->n);
-       /*
-        * Force accepting of the host key for localhost and 127.0.0.1. The
-        * problem is that if the home directory is NFS-mounted to multiple
-        * machines, localhost will refer to a different machine in each of
-        * them, and the user will get bogus HOST_CHANGED warnings.  This
-        * essentially disables host authentication for localhost; however,
-        * this is probably not a real problem.
-        */
-       if (local) {
-               debug("Forcing accepting of host key for localhost.");
-               host_status = HOST_OK;
-       }
        /*
         * Also perform check for the ip address, skip the check if we are
         * localhost or the hostname was an ip address to begin with
@@ -1200,10 +1189,10 @@ ssh_login(int host_key_valid,
                        char prompt[1024];
                        char *fp = fingerprint(host_key->e, host_key->n);
                        snprintf(prompt, sizeof(prompt),
-                                "The authenticity of host '%.200s' can't be established.\n"
-                                "Key fingerprint is %d %s.\n"
-                                "Are you sure you want to continue connecting (yes/no)? ",
-                                host, BN_num_bits(host_key->n), fp);
+                           "The authenticity of host '%.200s' can't be established.\n"
+                           "Key fingerprint is %d %s.\n"
+                           "Are you sure you want to continue connecting (yes/no)? ",
+                           host, BN_num_bits(host_key->n), fp);
                        if (!read_yes_or_no(prompt, -1))
                                fatal("Aborted by user!\n");
                }
@@ -1242,7 +1231,7 @@ ssh_login(int host_key_valid,
                }
                /* The host key has changed. */
                error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
-               error("@       WARNING: HOST IDENTIFICATION HAS CHANGED!         @");
+               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)!");
@@ -1280,9 +1269,121 @@ ssh_login(int host_key_valid,
                 */
                break;
        }
-
        if (options.check_host_ip)
                xfree(ip);
+}
+
+/*
+ * Starts a dialog with the server, and authenticates the current user on the
+ * server.  This does not need any extra privileges.  The basic connection
+ * to the server must already have been established before this is called.
+ * User is the remote user; if it is NULL, the current local user name will
+ * be used.  Anonymous indicates that no rhosts authentication will be used.
+ * If login fails, this function prints an error and never returns.
+ * 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)
+{
+       int i, type;
+       struct passwd *pw;
+       BIGNUM *key;
+       RSA *host_key;
+       RSA *public_key;
+       int bits, rbits;
+       unsigned char session_key[SSH_SESSION_KEY_LENGTH];
+       const char *server_user, *local_user;
+       char *host, *cp;
+       unsigned char check_bytes[8];
+       unsigned int supported_ciphers, supported_authentications;
+       unsigned int server_flags, client_flags;
+       int payload_len, clen, sum_len = 0;
+       u_int32_t rand = 0;
+
+       /* Convert the user-supplied hostname into all lowercase. */
+       host = xstrdup(orighost);
+       for (cp = host; *cp; cp++)
+               if (isupper(*cp))
+                       *cp = tolower(*cp);
+
+       /* Exchange protocol version identification strings with the server. */
+       ssh_exchange_identification();
+
+       /* Put the connection into non-blocking mode. */
+       packet_set_nonblocking();
+
+       /* 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;
+
+       debug("Waiting for server public key.");
+
+       /* Wait for a public key packet from the server. */
+       packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY);
+
+       /* Get check bytes from the packet. */
+       for (i = 0; i < 8; i++)
+               check_bytes[i] = packet_get_char();
+
+       /* Get the public key. */
+       public_key = RSA_new();
+       bits = packet_get_int();/* bits */
+       public_key->e = BN_new();
+       packet_get_bignum(public_key->e, &clen);
+       sum_len += clen;
+       public_key->n = BN_new();
+       packet_get_bignum(public_key->n, &clen);
+       sum_len += clen;
+
+       rbits = BN_num_bits(public_key->n);
+       if (bits != rbits) {
+               log("Warning: Server lies about size of server public key: "
+                   "actual size is %d bits vs. announced %d.", rbits, bits);
+               log("Warning: This may be due to an old implementation of ssh.");
+       }
+       /* Get the host key. */
+       host_key = RSA_new();
+       bits = packet_get_int();/* bits */
+       host_key->e = BN_new();
+       packet_get_bignum(host_key->e, &clen);
+       sum_len += clen;
+       host_key->n = BN_new();
+       packet_get_bignum(host_key->n, &clen);
+       sum_len += clen;
+
+       rbits = BN_num_bits(host_key->n);
+       if (bits != rbits) {
+               log("Warning: Server lies about size of server host key: "
+                   "actual size is %d bits vs. announced %d.", rbits, bits);
+               log("Warning: This may be due to an old implementation of ssh.");
+       }
+
+       /* Get protocol flags. */
+       server_flags = packet_get_int();
+       packet_set_protocol_flags(server_flags);
+
+       supported_ciphers = packet_get_int();
+       supported_authentications = packet_get_int();
+
+       debug("Received server public key (%d bits) and host key (%d bits).",
+             BN_num_bits(public_key->n), BN_num_bits(host_key->n));
+
+       packet_integrity_check(payload_len,
+                              8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4,
+                              SSH_SMSG_PUBLIC_KEY);
+
+       check_host_key(host, hostaddr, host_key);
+
+       client_flags = SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN;
+
+       compute_session_id(session_id, check_bytes, host_key->n, public_key->n);
 
        /* Generate a session key. */
        arc4random_stir();
@@ -1373,7 +1474,7 @@ ssh_login(int host_key_valid,
        packet_put_bignum(key);
 
        /* Send protocol flags. */
-       packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN);
+       packet_put_int(client_flags);
 
        /* Send the packet now. */
        packet_send();
@@ -1494,80 +1595,24 @@ ssh_login(int host_key_valid,
 
                /* Try RSA authentication for each identity. */
                for (i = 0; i < options.num_identity_files; i++)
-                       if (try_rsa_authentication(pw, options.identity_files[i]))
+                       if (try_rsa_authentication(options.identity_files[i]))
                                return;
        }
        /* Try skey authentication if the server supports it. */
        if ((supported_authentications & (1 << SSH_AUTH_TIS)) &&
            options.skey_authentication && !options.batch_mode) {
-               debug("Doing skey authentication.");
-
-               /* request a challenge */
-               packet_start(SSH_CMSG_AUTH_TIS);
-               packet_send();
-               packet_write_wait();
-
-               type = packet_read(&payload_len);
-               if (type != SSH_SMSG_FAILURE &&
-                   type != SSH_SMSG_AUTH_TIS_CHALLENGE) {
-                       packet_disconnect("Protocol error: got %d in response "
-                                         "to skey auth", type);
-               }
-               if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) {
-                       debug("No challenge for skey authentication.");
-               } else {
-                       char *challenge, *response;
-                       challenge = packet_get_string(&payload_len);
-                       if (options.cipher == SSH_CIPHER_NONE)
-                               log("WARNING: Encryption is disabled! "
-                                   "Reponse will be transmitted in clear text.");
-                       fprintf(stderr, "%s\n", challenge);
-                       fflush(stderr);
-                       for (i = 0; i < options.number_of_password_prompts; i++) {
-                               if (i != 0)
-                                       error("Permission denied, please try again.");
-                               response = read_passphrase("Response: ", 0);
-                               packet_start(SSH_CMSG_AUTH_TIS_RESPONSE);
-                               packet_put_string(response, strlen(response));
-                               memset(response, 0, strlen(response));
-                               xfree(response);
-                               packet_send();
-                               packet_write_wait();
-                               type = packet_read(&payload_len);
-                               if (type == SSH_SMSG_SUCCESS)
-                                       return;
-                               if (type != SSH_SMSG_FAILURE)
-                                       packet_disconnect("Protocol error: got %d in response "
-                                                         "to skey auth", type);
-                       }
-               }
+               if (try_skey_authentication())
+                       return;
        }
        /* Try password authentication if the server supports it. */
        if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) &&
            options.password_authentication && !options.batch_mode) {
                char prompt[80];
-               snprintf(prompt, sizeof(prompt), "%.30s@%.30s's password: ",
-                        server_user, host);
-               debug("Doing password authentication.");
-               if (options.cipher == SSH_CIPHER_NONE)
-                       log("WARNING: Encryption is disabled! Password will be transmitted in clear text.");
-               for (i = 0; i < options.number_of_password_prompts; i++) {
-                       if (i != 0)
-                               error("Permission denied, please try again.");
-                       password = read_passphrase(prompt, 0);
-                       packet_start(SSH_CMSG_AUTH_PASSWORD);
-                       packet_put_string(password, strlen(password));
-                       memset(password, 0, strlen(password));
-                       xfree(password);
-                       packet_send();
-                       packet_write_wait();
 
-                       type = packet_read(&payload_len);
-                       if (type == SSH_SMSG_SUCCESS)
-                               return;
-                       if (type != SSH_SMSG_FAILURE)
-                               packet_disconnect("Protocol error: got %d in response to passwd auth", type);
-               }
+               snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ",
+                   server_user, host);
+               if (try_password_authentication(prompt))
+                       return;
        }
        /* All authentication methods have failed.  Exit with an error message. */
        fatal("Permission denied.");
This page took 0.241159 seconds and 4 git commands to generate.