]> andersk Git - openssh.git/blobdiff - sshd.c
- Update to latest OpenBSD CVS:
[openssh.git] / sshd.c
diff --git a/sshd.c b/sshd.c
index 14642abd198ee212061fef1d075d31a8a7569d3b..03a9ce120891e555f3c602fdb3b72d61e371f667 100644 (file)
--- a/sshd.c
+++ b/sshd.c
  */
 
 #include "includes.h"
-RCSID("$Id$");
-
-#ifdef HAVE_POLL_H
-# include <poll.h>
-#else /* HAVE_POLL_H */
-# ifdef HAVE_SYS_POLL_H
-#  include <sys/poll.h>
-# endif /* HAVE_SYS_POLL_H */
-#endif /* HAVE_POLL_H */
+RCSID("$OpenBSD: sshd.c,v 1.79 2000/01/18 13:45:05 markus Exp $");
 
 #include "xmalloc.h"
 #include "rsa.h"
@@ -53,6 +45,16 @@ ServerOptions options;
 /* Name of the server configuration file. */
 char *config_file_name = SERVER_CONFIG_FILE;
 
+/* 
+ * Flag indicating whether IPv4 or IPv6.  This can be set on the command line.
+ * Default value is AF_UNSPEC means both IPv4 and IPv6.
+ */
+#ifdef IPV4_DEFAULT
+int IPv4or6 = AF_INET;
+#else
+int IPv4or6 = AF_UNSPEC;
+#endif
+
 /*
  * Debug mode flag.  This can be set on the command line.  If debug
  * mode is enabled, extra debugging output will be sent to the system
@@ -74,10 +76,12 @@ char *av0;
 char **saved_argv;
 
 /*
- * This is set to the socket that the server is listening; this is used in
- * the SIGHUP signal handler.
+ * The sockets that the server is listening; this is used in the SIGHUP
+ * signal handler.
  */
-int listen_sock;
+#define        MAX_LISTEN_SOCKS        16
+int listen_socks[MAX_LISTEN_SOCKS];
+int num_listen_socks = 0;
 
 /*
  * the client's version string, passed by sshd2 in compat mode. if != NULL,
@@ -127,8 +131,8 @@ int received_sighup = 0;
 RSA *public_key;
 
 /* Prototypes for various functions defined later in this file. */
-void do_connection();
-void do_authentication(char *user);
+void do_ssh_kex();
+void do_authentication();
 void do_authloop(struct passwd * pw);
 void do_fake_authloop(char *user);
 void do_authenticated(struct passwd * pw);
@@ -143,165 +147,17 @@ void do_child(const char *command, struct passwd * pw, const char *term,
              const char *display, const char *auth_proto,
              const char *auth_data, const char *ttyname);
 
-#ifdef USE_PAM
-static int pamconv(int num_msg, const struct pam_message **msg,
-         struct pam_response **resp, void *appdata_ptr);
-int do_pam_auth(const char *user, const char *password);
-void do_pam_account(char *username, char *remote_user);
-void do_pam_session(char *username, char *ttyname);
-void pam_cleanup_proc(void *context);
-
-static struct pam_conv conv = {
-       pamconv,
-       NULL
-};
-struct pam_handle_t *pamh = NULL;
-const char *pampasswd = NULL;
-char *pamconv_msg = NULL;
-
-static int pamconv(int num_msg, const struct pam_message **msg,
-       struct pam_response **resp, void *appdata_ptr)
-{
-       struct pam_response *reply;
-       int count;
-       size_t msg_len;
-       char *p;
-
-       /* PAM will free this later */
-       reply = malloc(num_msg * sizeof(*reply));
-       if (reply == NULL)
-               return PAM_CONV_ERR; 
-
-       for(count = 0; count < num_msg; count++) {
-               switch (msg[count]->msg_style) {
-                       case PAM_PROMPT_ECHO_OFF:
-                               if (pampasswd == NULL) {
-                                       free(reply);
-                                       return PAM_CONV_ERR;
-                               }
-                               reply[count].resp_retcode = PAM_SUCCESS;
-                               reply[count].resp = xstrdup(pampasswd);
-                               break;
-
-                       case PAM_TEXT_INFO:
-                               reply[count].resp_retcode = PAM_SUCCESS;
-                               reply[count].resp = xstrdup("");
-
-                               if (msg[count]->msg == NULL)
-                                       break;
-
-                               debug("Adding PAM message: %s", msg[count]->msg);
-
-                               msg_len = strlen(msg[count]->msg);
-                               if (pamconv_msg) {
-                                       size_t n = strlen(pamconv_msg);
-                                       pamconv_msg = xrealloc(pamconv_msg, n + msg_len + 2);
-                                       p = pamconv_msg + n;
-                               } else {
-                                       pamconv_msg = p = xmalloc(msg_len + 2);
-                               }
-                               memcpy(p, msg[count]->msg, msg_len);
-                               p[msg_len] = '\n';
-                               p[msg_len + 1] = '\0';
-                               break;
-
-                       case PAM_PROMPT_ECHO_ON:
-                       case PAM_ERROR_MSG:
-                       default:
-                               free(reply);
-                               return PAM_CONV_ERR;
-               }
-       }
-
-       *resp = reply;
-
-       return PAM_SUCCESS;
-}
-
-void pam_cleanup_proc(void *context)
-{
-       int pam_retval;
-
-       if (pamh != NULL)
-       {
-               pam_retval = pam_close_session((pam_handle_t *)pamh, 0);
-               if (pam_retval != PAM_SUCCESS) {
-                       log("Cannot close PAM session: %.200s", 
-                       PAM_STRERROR((pam_handle_t *)pamh, pam_retval));
-               }
-
-               pam_retval = pam_end((pam_handle_t *)pamh, pam_retval);
-               if (pam_retval != PAM_SUCCESS) {
-                       log("Cannot release PAM authentication: %.200s", 
-                       PAM_STRERROR((pam_handle_t *)pamh, pam_retval));
-               }
-       }
-}
-
-int do_pam_auth(const char *user, const char *password)
-{
-       int pam_retval;
-       
-       if ((options.permit_empty_passwd == 0) && (password[0] == '\0'))
-               return 0;
-
-       pampasswd = password;
-       
-       pam_retval = pam_authenticate((pam_handle_t *)pamh, 0);
-       if (pam_retval == PAM_SUCCESS) {
-               debug("PAM Password authentication accepted for user \"%.100s\"", user);
-               return 1;
-       } else {
-               debug("PAM Password authentication for \"%.100s\" failed: %s", 
-                       user, PAM_STRERROR((pam_handle_t *)pamh, pam_retval));
-               return 0;
-       }
-}
-
-void do_pam_account(char *username, char *remote_user)
-{
-       int pam_retval;
-
-       debug("PAM setting rhost to \"%.200s\"", get_canonical_hostname());
-       pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RHOST, 
-               get_canonical_hostname());
-       if (pam_retval != PAM_SUCCESS) {
-               log("PAM set rhost failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval));
-               do_fake_authloop(username);
-       }
-
-       if (remote_user != NULL) {
-               debug("PAM setting ruser to \"%.200s\"", remote_user);
-               pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RUSER, remote_user);
-               if (pam_retval != PAM_SUCCESS) {
-                       log("PAM set ruser failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval));
-                       do_fake_authloop(username);
-               }
-       }
-
-       pam_retval = pam_acct_mgmt((pam_handle_t *)pamh, 0);
-       if (pam_retval != PAM_SUCCESS) {
-               log("PAM rejected by account configuration: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval));
-               do_fake_authloop(username);
-       }
-}
-
-void do_pam_session(char *username, char *ttyname)
+/*
+ * Close all listening sockets
+ */
+void
+close_listen_socks(void)
 {
-       int pam_retval;
-
-       if (ttyname != NULL) {
-               debug("PAM setting tty to \"%.200s\"", ttyname);
-               pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_TTY, ttyname);
-               if (pam_retval != PAM_SUCCESS)
-                       fatal("PAM set tty failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval));
-       }
-
-       pam_retval = pam_open_session((pam_handle_t *)pamh, 0);
-       if (pam_retval != PAM_SUCCESS)
-               fatal("PAM session setup failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval));
+       int i;
+       for (i = 0; i < num_listen_socks; i++)
+               close(listen_socks[i]);
+       num_listen_socks = -1;
 }
-#endif /* USE_PAM */
 
 /*
  * Signal handler for SIGHUP.  Sshd execs itself when it receives SIGHUP;
@@ -323,7 +179,7 @@ void
 sighup_restart()
 {
        log("Received SIGHUP; restarting.");
-       close(listen_sock);
+       close_listen_socks();
        execv(saved_argv[0], saved_argv);
        log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno));
        exit(1);
@@ -338,7 +194,7 @@ void
 sigterm_handler(int sig)
 {
        log("Received signal %d; terminating.", sig);
-       close(listen_sock);
+       close_listen_socks();
        exit(255);
 }
 
@@ -445,11 +301,12 @@ main(int ac, char **av)
 {
        extern char *optarg;
        extern int optind;
-       int opt, aux, sock_in, sock_out, newsock, i, pid, on = 1;
+       int opt, sock_in = 0, sock_out = 0, newsock, i, fdsetsz, pid, on = 1;
+       socklen_t fromlen;
        int remote_major, remote_minor;
        int silentrsa = 0;
-       struct pollfd fds;
-       struct sockaddr_in sin;
+       fd_set *fdset;
+       struct sockaddr_storage from;
        char buf[100];                  /* Must not be larger than remote_version. */
        char remote_version[100];       /* Must be at least as big as buf. */
        const char *remote_ip;
@@ -457,6 +314,9 @@ main(int ac, char **av)
        char *comment;
        FILE *f;
        struct linger linger;
+       struct addrinfo *ai;
+       char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+       int listen_sock, maxfd;
 
        /* Save argv[0]. */
        saved_argv = av;
@@ -469,8 +329,14 @@ main(int ac, char **av)
        initialize_server_options(&options);
 
        /* Parse command-line arguments. */
-       while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ")) != EOF) {
+       while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ46")) != EOF) {
                switch (opt) {
+               case '4':
+                       IPv4or6 = AF_INET;
+                       break;
+               case '6':
+                       IPv4or6 = AF_INET6;
+                       break;
                case 'f':
                        config_file_name = optarg;
                        break;
@@ -491,7 +357,10 @@ main(int ac, char **av)
                        options.server_key_bits = atoi(optarg);
                        break;
                case 'p':
-                       options.port = atoi(optarg);
+                       options.ports_from_cmdline = 1;
+                       if (options.num_ports >= MAX_PORTS)
+                               fatal("too many ports.\n");
+                       options.ports[options.num_ports++] = atoi(optarg);
                        break;
                case 'g':
                        options.login_grace_time = atoi(optarg);
@@ -510,6 +379,9 @@ main(int ac, char **av)
                case '?':
                default:
                        fprintf(stderr, "sshd version %s\n", SSH_VERSION);
+#ifdef RSAREF
+                       fprintf(stderr, "Compiled with RSAref.\n");
+#endif
                        fprintf(stderr, "Usage: %s [options]\n", av0);
                        fprintf(stderr, "Options:\n");
                        fprintf(stderr, "  -f file    Configuration file (default %s)\n", SERVER_CONFIG_FILE);
@@ -521,11 +393,22 @@ main(int ac, char **av)
                        fprintf(stderr, "  -g seconds Grace period for authentication (default: 300)\n");
                        fprintf(stderr, "  -b bits    Size of server RSA key (default: 768 bits)\n");
                        fprintf(stderr, "  -h file    File from which to read host key (default: %s)\n",
-                               HOST_KEY_FILE);
+                           HOST_KEY_FILE);
+                       fprintf(stderr, "  -4         Use IPv4 only\n");
+                       fprintf(stderr, "  -6         Use IPv6 only\n");
                        exit(1);
                }
        }
 
+       /*
+        * Force logging to stderr until we have loaded the private host
+        * key (unless started from inetd)
+        */
+       log_init(av0,
+           options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level,
+           options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility,
+           !inetd_flag);
+
        /* check if RSA support exists */
        if (rsa_alive() == 0) {
                if (silentrsa == 0)
@@ -545,18 +428,11 @@ main(int ac, char **av)
                fprintf(stderr, "Bad server key size.\n");
                exit(1);
        }
-       if (options.port < 1 || options.port > 65535) {
-               fprintf(stderr, "Bad port number.\n");
-               exit(1);
-       }
        /* Check that there are no remaining arguments. */
        if (optind < ac) {
                fprintf(stderr, "Extra argument %s.\n", av[optind]);
                exit(1);
        }
-       /* Force logging to stderr while loading the private host key
-          unless started from inetd */
-       log_init(av0, options.log_level, options.log_facility, !inetd_flag);
 
        debug("sshd version %.100s", SSH_VERSION);
 
@@ -645,32 +521,66 @@ main(int ac, char **av)
                arc4random_stir();
                log("RSA key generation complete.");
        } else {
-               /* Create socket for listening. */
-               listen_sock = socket(AF_INET, SOCK_STREAM, 0);
-               if (listen_sock < 0)
-                       fatal("socket: %.100s", strerror(errno));
-
-               /* Set socket options.  We try to make the port reusable
-                  and have it close as fast as possible without waiting
-                  in unnecessary wait states on close. */
-               setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on,
-                          sizeof(on));
-               linger.l_onoff = 1;
-               linger.l_linger = 5;
-               setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (void *) &linger,
-                          sizeof(linger));
-
-               memset(&sin, 0, sizeof(sin));
-               sin.sin_family = AF_INET;
-               sin.sin_addr = options.listen_addr;
-               sin.sin_port = htons(options.port);
-
-               if (bind(listen_sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) {
-                       error("bind: %.100s", strerror(errno));
-                       shutdown(listen_sock, SHUT_RDWR);
-                       close(listen_sock);
-                       fatal("Bind to port %d failed.", options.port);
+               for (ai = options.listen_addrs; ai; ai = ai->ai_next) {
+                       if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+                               continue;
+                       if (num_listen_socks >= MAX_LISTEN_SOCKS)
+                               fatal("Too many listen sockets. "
+                                   "Enlarge MAX_LISTEN_SOCKS");
+                       if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
+                           ntop, sizeof(ntop), strport, sizeof(strport),
+                           NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
+                               error("getnameinfo failed");
+                               continue;
+                       }
+                       /* Create socket for listening. */
+                       listen_sock = socket(ai->ai_family, SOCK_STREAM, 0);
+                       if (listen_sock < 0) {
+                               /* kernel may not support ipv6 */
+                               verbose("socket: %.100s", strerror(errno));
+                               continue;
+                       }
+                       if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) < 0) {
+                               error("listen_sock O_NONBLOCK: %s", strerror(errno));
+                               close(listen_sock);
+                               continue;
+                       }
+                       /*
+                        * Set socket options.  We try to make the port
+                        * reusable and have it close as fast as possible
+                        * without waiting in unnecessary wait states on
+                        * close.
+                        */
+                       setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
+                           (void *) &on, sizeof(on));
+                       linger.l_onoff = 1;
+                       linger.l_linger = 5;
+                       setsockopt(listen_sock, SOL_SOCKET, SO_LINGER,
+                           (void *) &linger, sizeof(linger));
+
+                       debug("Bind to port %s on %s.", strport, ntop);
+
+                       /* Bind the socket to the desired port. */
+                       if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) {
+                               error("Bind to port %s on %s failed: %.200s.",
+                                   strport, ntop, strerror(errno));
+                               close(listen_sock);
+                               continue;
+                       }
+                       listen_socks[num_listen_socks] = listen_sock;
+                       num_listen_socks++;
+
+                       /* Start listening on the port. */
+                       log("Server listening on %s port %s.", ntop, strport);
+                       if (listen(listen_sock, 5) < 0)
+                               fatal("listen: %.100s", strerror(errno));
+
                }
+               freeaddrinfo(options.listen_addrs);
+
+               if (!num_listen_socks)
+                       fatal("Cannot bind any address.");
+
                if (!debug_flag) {
                        /*
                         * Record our pid in /etc/sshd_pid to make it easier
@@ -686,10 +596,6 @@ main(int ac, char **av)
                        }
                }
 
-               log("Server listening on port %d.", options.port);
-               if (listen(listen_sock, 5) < 0)
-                       fatal("listen: %.100s", strerror(errno));
-
                public_key = RSA_new();
                sensitive_data.private_key = RSA_new();
 
@@ -711,6 +617,14 @@ main(int ac, char **av)
                /* Arrange SIGCHLD to be caught. */
                signal(SIGCHLD, main_sigchld_handler);
 
+               /* setup fd set for listen */
+               maxfd = 0;
+               for (i = 0; i < num_listen_socks; i++)
+                       if (listen_socks[i] > maxfd)
+                               maxfd = listen_socks[i];
+               fdsetsz = howmany(maxfd, NFDBITS) * sizeof(fd_mask);         
+               fdset = (fd_set *)xmalloc(fdsetsz);                                  
+
                /*
                 * Stay listening for connections until the system crashes or
                 * the daemon is killed with a signal.
@@ -718,26 +632,28 @@ main(int ac, char **av)
                for (;;) {
                        if (received_sighup)
                                sighup_restart();
-                       /* Wait in poll until there is a connection. */
-                       memset(&fds, 0, sizeof(fds));
-                       fds.fd = listen_sock;
-                       fds.events = POLLIN;
-                       if (poll(&fds, 1, -1) == -1) {
-                               if (errno == EINTR)
-                                       continue;
-                               fatal("poll: %.100s", strerror(errno));
-                               /*NOTREACHED*/
-                       }
-                       if (fds.revents == 0)
+                       /* Wait in select until there is a connection. */
+                       memset(fdset, 0, fdsetsz);
+                       for (i = 0; i < num_listen_socks; i++)
+                               FD_SET(listen_socks[i], fdset);
+                       if (select(maxfd + 1, fdset, NULL, NULL, NULL) < 0) {
+                               if (errno != EINTR)
+                                       error("select: %.100s", strerror(errno));
                                continue;
-                       aux = sizeof(sin);
-                       newsock = accept(listen_sock, (struct sockaddr *) & sin, &aux);
-                       if (received_sighup)
-                               sighup_restart();
-                       if (newsock < 0) {
-                               if (errno == EINTR)
+                       }
+                       for (i = 0; i < num_listen_socks; i++) {
+                               if (!FD_ISSET(listen_socks[i], fdset))
                                        continue;
-                               error("accept: %.100s", strerror(errno));
+                       fromlen = sizeof(from);
+                       newsock = accept(listen_socks[i], (struct sockaddr *)&from,
+                           &fromlen);
+                       if (newsock < 0) {
+                               if (errno != EINTR && errno != EWOULDBLOCK)
+                                       error("accept: %.100s", strerror(errno));
+                               continue;
+                       }
+                       if (fcntl(newsock, F_SETFL, 0) < 0) {
+                               error("newsock del O_NONBLOCK: %s", strerror(errno));
                                continue;
                        }
                        /*
@@ -751,7 +667,7 @@ main(int ac, char **av)
                                 * connection without forking.
                                 */
                                debug("Server will not fork when running in debugging mode.");
-                               close(listen_sock);
+                               close_listen_socks();
                                sock_in = newsock;
                                sock_out = newsock;
                                pid = getpid();
@@ -768,7 +684,7 @@ main(int ac, char **av)
                                         * accepted socket.  Reinitialize logging (since our pid has
                                         * changed).  We break out of the loop to handle the connection.
                                         */
-                                       close(listen_sock);
+                                       close_listen_socks();
                                        sock_in = newsock;
                                        sock_out = newsock;
                                        log_init(av0, options.log_level, options.log_facility, log_stderr);
@@ -789,6 +705,10 @@ main(int ac, char **av)
 
                        /* Close the new socket (the child is now taking care of it). */
                        close(newsock);
+                       } /* for (i = 0; i < num_listen_socks; i++) */
+                       /* child process check (or debug mode) */
+                       if (num_listen_socks < 0)
+                               break;
                }
        }
 
@@ -827,6 +747,7 @@ main(int ac, char **av)
 
        /* Check whether logins are denied from this host. */
 #ifdef LIBWRAP
+       /* XXX LIBWRAP noes not know about IPv6 */
        {
                struct request_info req;
 
@@ -838,12 +759,11 @@ main(int ac, char **av)
                        close(sock_out);
                        refuse(&req);
                }
-               verbose("Connection from %.500s port %d", eval_client(&req), remote_port);
+/*XXX IPv6 verbose("Connection from %.500s port %d", eval_client(&req), remote_port); */
        }
-#else
+#endif /* LIBWRAP */
        /* Log the connection. */
        verbose("Connection from %.500s port %d", remote_ip, remote_port);
-#endif /* LIBWRAP */
 
        /*
         * We don\'t want to listen forever unless the other side
@@ -865,12 +785,12 @@ main(int ac, char **av)
                snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
                         PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
                if (atomicio(write, sock_out, buf, strlen(buf)) != strlen(buf))
-                       fatal("Could not write ident string to %s.", get_remote_ipaddr());
+                       fatal("Could not write ident string to %s.", remote_ip);
 
                /* Read other side\'s version identification. */
                for (i = 0; i < sizeof(buf) - 1; i++) {
                        if (read(sock_in, &buf[i], 1) != 1)
-                               fatal("Did not receive ident string from %s.", get_remote_ipaddr());
+                               fatal("Did not receive ident string from %s.", remote_ip);
                        if (buf[i] == '\r') {
                                buf[i] = '\n';
                                buf[i + 1] = 0;
@@ -897,7 +817,7 @@ main(int ac, char **av)
                close(sock_in);
                close(sock_out);
                fatal("Bad protocol version identification '%.100s' from %s",
-                     buf, get_remote_ipaddr());
+                     buf, remote_ip);
        }
        debug("Client protocol version %d.%d; client software version %.100s",
              remote_major, remote_minor, remote_version);
@@ -908,19 +828,15 @@ main(int ac, char **av)
                close(sock_in);
                close(sock_out);
                fatal("Protocol major versions differ for %s: %d vs. %d",
-                     get_remote_ipaddr(),
-                     PROTOCOL_MAJOR, remote_major);
+                     remote_ip, PROTOCOL_MAJOR, remote_major);
        }
        /* Check that the client has sufficiently high software version. */
        if (remote_major == 1 && remote_minor < 3)
                packet_disconnect("Your ssh version is too old and is no longer supported.  Please install a newer version.");
 
        if (remote_major == 1 && remote_minor == 3) {
+               /* note that this disables agent-forwarding */
                enable_compat13();
-               if (strcmp(remote_version, "OpenSSH-1.1") != 0) {
-                       debug("Agent forwarding disabled, remote version is not compatible.");
-                       no_agent_forwarding_flag = 1;
-               }
        }
        /*
         * Check that the connection comes from a privileged port.  Rhosts-
@@ -934,10 +850,21 @@ main(int ac, char **av)
                options.rhosts_authentication = 0;
                options.rhosts_rsa_authentication = 0;
        }
+#ifdef KRB4
+       if (!packet_connection_is_ipv4() &&
+           options.kerberos_authentication) {
+               debug("Kerberos Authentication disabled, only available for IPv4.");
+               options.kerberos_authentication = 0;
+       }
+#endif /* KRB4 */
+
        packet_set_nonblocking();
 
-       /* Handle the connection. */
-       do_connection();
+       /* perform the key exchange */
+       do_ssh_kex();
+
+       /* authenticate user and start session */
+       do_authentication();
 
 #ifdef KRB4
        /* Cleanup user's ticket cache file. */
@@ -953,20 +880,7 @@ main(int ac, char **av)
        verbose("Closing connection to %.100s", remote_ip);
 
 #ifdef USE_PAM
-       {
-               int retval;
-
-               if (pamh != NULL) {
-                       debug("Closing PAM session.");
-                       retval = pam_close_session((pam_handle_t *)pamh, 0);
-
-                       debug("Terminating PAM library.");
-                       if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS)
-                               log("Cannot release PAM authentication.");
-
-                       fatal_remove_cleanup(&pam_cleanup_proc, NULL);
-               }
-       }
+       finish_pam();
 #endif /* USE_PAM */
 
        packet_close();
@@ -974,20 +888,17 @@ main(int ac, char **av)
 }
 
 /*
- * Process an incoming connection.  Protocol version identifiers have already
- * been exchanged.  This sends server key and performs the key exchange.
- * Server and host keys will no longer be needed after this functions.
+ * SSH1 key exchange
  */
 void
-do_connection()
+do_ssh_kex()
 {
        int i, len;
+       int plen, slen;
        BIGNUM *session_key_int;
        unsigned char session_key[SSH_SESSION_KEY_LENGTH];
-       unsigned char check_bytes[8];
-       char *user;
+       unsigned char cookie[8];
        unsigned int cipher_type, auth_mask, protocol_flags;
-       int plen, slen, ulen;
        u_int32_t rand = 0;
 
        /*
@@ -1002,7 +913,7 @@ do_connection()
        for (i = 0; i < 8; i++) {
                if (i % 4 == 0)
                        rand = arc4random();
-               check_bytes[i] = rand & 0xff;
+               cookie[i] = rand & 0xff;
                rand >>= 8;
        }
 
@@ -1013,7 +924,7 @@ do_connection()
         */
        packet_start(SSH_SMSG_PUBLIC_KEY);
        for (i = 0; i < 8; i++)
-               packet_put_char(check_bytes[i]);
+               packet_put_char(cookie[i]);
 
        /* Store our public server RSA key. */
        packet_put_int(BN_num_bits(public_key->n));
@@ -1076,7 +987,7 @@ do_connection()
        /* Get check bytes from the packet.  These must match those we
           sent earlier with the public key packet. */
        for (i = 0; i < 8; i++)
-               if (check_bytes[i] != packet_get_char())
+               if (cookie[i] != packet_get_char())
                        packet_disconnect("IP Spoofing check bytes do not match.");
 
        debug("Encryption type: %.200s", cipher_name(cipher_type));
@@ -1124,10 +1035,15 @@ do_connection()
                                    sensitive_data.private_key);
        }
 
-       compute_session_id(session_id, check_bytes,
+       compute_session_id(session_id, cookie,
                           sensitive_data.host_key->n,
                           sensitive_data.private_key->n);
 
+       /* Destroy the private and public keys.  They will no longer be needed. */
+       RSA_free(public_key);
+       RSA_free(sensitive_data.private_key);
+       RSA_free(sensitive_data.host_key);
+
        /*
         * Extract session key from the decrypted integer.  The key is in the
         * least significant 256 bits of the integer; the first byte of the
@@ -1142,13 +1058,13 @@ do_connection()
        memset(session_key, 0, sizeof(session_key));
        BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len);
 
+       /* Destroy the decrypted integer.  It is no longer needed. */
+       BN_clear_free(session_key_int);
+
        /* Xor the first 16 bytes of the session key with the session id. */
        for (i = 0; i < 16; i++)
                session_key[i] ^= session_id[i];
 
-       /* Destroy the decrypted integer.  It is no longer needed. */
-       BN_clear_free(session_key_int);
-
        /* Set the session key.  From this on all communications will be encrypted. */
        packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type);
 
@@ -1161,24 +1077,9 @@ do_connection()
        packet_start(SSH_SMSG_SUCCESS);
        packet_send();
        packet_write_wait();
-
-       /* Get the name of the user that we wish to log in as. */
-       packet_read_expect(&plen, SSH_CMSG_USER);
-
-       /* Get the user name. */
-       user = packet_get_string(&ulen);
-       packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER);
-
-       /* Destroy the private and public keys.  They will no longer be needed. */
-       RSA_free(public_key);
-       RSA_free(sensitive_data.private_key);
-       RSA_free(sensitive_data.host_key);
-
-       setproctitle("%s", user);
-       /* Do the authentication. */
-       do_authentication(user);
 }
 
+
 /*
  * Check if the user is allowed to log in via ssh. If user is listed in
  * DenyUsers or user's primary group is listed in DenyGroups, false will
@@ -1254,13 +1155,23 @@ allowed_user(struct passwd * pw)
 
 /*
  * Performs authentication of an incoming connection.  Session key has already
- * been exchanged and encryption is enabled.  User is the user name to log
- * in as (received from the client).
+ * been exchanged and encryption is enabled.
  */
 void
-do_authentication(char *user)
+do_authentication()
 {
        struct passwd *pw, pwcopy;
+       int plen, ulen;
+       char *user;
+
+       /* Get the name of the user that we wish to log in as. */
+       packet_read_expect(&plen, SSH_CMSG_USER);
+
+       /* Get the user name. */
+       user = packet_get_string(&ulen);
+       packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER);
+
+       setproctitle("%s", user);
 
 #ifdef AFS
        /* If machine has AFS, set process authentication group. */
@@ -1286,17 +1197,7 @@ do_authentication(char *user)
        pw = &pwcopy;
 
 #ifdef USE_PAM
-       {
-               int pam_retval;
-
-               debug("Starting up PAM with username \"%.200s\"", pw->pw_name);
-
-               pam_retval = pam_start("sshd", pw->pw_name, &conv, (pam_handle_t**)&pamh);
-               if (pam_retval != PAM_SUCCESS)
-                       fatal("PAM initialisation failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval));
-
-               fatal_add_cleanup(&pam_cleanup_proc, NULL);
-       }
+       start_pam(pw);
 #endif
 
        /*
@@ -1314,7 +1215,7 @@ do_authentication(char *user)
            (!options.kerberos_authentication || options.kerberos_or_local_passwd) &&
 #endif /* KRB4 */
 #ifdef USE_PAM
-           do_pam_auth(pw->pw_name, "")) {
+           auth_pam_password(pw, "")) {
 #else /* USE_PAM */
            auth_password(pw, "")) {
 #endif /* USE_PAM */
@@ -1457,9 +1358,6 @@ do_authloop(struct passwd * pw)
                        authenticated = auth_rhosts(pw, client_user);
 
                        snprintf(user, sizeof user, " ruser %s", client_user);
-#ifndef USE_PAM
-                       xfree(client_user);
-#endif /* USE_PAM */
                        break;
 
                case SSH_CMSG_AUTH_RHOSTS_RSA:
@@ -1492,9 +1390,6 @@ do_authloop(struct passwd * pw)
                        BN_clear_free(client_host_key_n);
 
                        snprintf(user, sizeof user, " ruser %s", client_user);
-#ifndef USE_PAM
-                       xfree(client_user);
-#endif /* USE_PAM */
                        break;
 
                case SSH_CMSG_AUTH_RSA:
@@ -1525,7 +1420,7 @@ do_authloop(struct passwd * pw)
 
 #ifdef USE_PAM
                        /* Do PAM auth with password */
-                       authenticated = do_pam_auth(pw->pw_name, password);
+                       authenticated = auth_pam_password(pw, password);
 #else /* USE_PAM */
                        /* Try authentication with the password. */
                        authenticated = auth_password(pw, password);
@@ -1595,29 +1490,24 @@ do_authloop(struct passwd * pw)
                        get_remote_port(),
                        user);
 
-#ifndef USE_PAM
-               if (authenticated)
-                       return;
-
-               if (attempt > AUTH_FAIL_MAX)
-                       packet_disconnect(AUTH_FAIL_MSG, pw->pw_name);
-#else /* USE_PAM */
                if (authenticated) {
-                       do_pam_account(pw->pw_name, client_user);
-
-                       if (client_user != NULL)
-                               xfree(client_user);
+#ifdef USE_PAM
+                       if (!do_pam_account(pw->pw_name, client_user))
+                       {
+                               if (client_user != NULL)
+                                       xfree(client_user);
 
+                               do_fake_authloop(pw->pw_name);
+                       }
+#endif /* USE_PAM */
                        return;
                }
 
-               if (attempt > AUTH_FAIL_MAX) {
-                       if (client_user != NULL)
-                               xfree(client_user);
+               if (client_user != NULL)
+                       xfree(client_user);
 
+               if (attempt > AUTH_FAIL_MAX)
                        packet_disconnect(AUTH_FAIL_MSG, pw->pw_name);
-               }
-#endif /* USE_PAM */
 
                /* Send a message indicating that the authentication attempt failed. */
                packet_start(SSH_SMSG_FAILURE);
@@ -1652,8 +1542,10 @@ do_fake_authloop(char *user)
        for (attempt = 1;; attempt++) {
                /* Read a packet.  This will not return if the client disconnects. */
                int plen;
+#ifndef SKEY
+               (void)packet_read(&plen);
+#else /* SKEY */
                int type = packet_read(&plen);
-#ifdef SKEY
                int dlen;
                char *password, *skeyinfo;
                /* Try to send a fake s/key challenge. */
@@ -1825,7 +1717,7 @@ do_authenticated(struct passwd * pw)
 
 #ifdef USE_PAM
                        /* do the pam_open_session since we have the pty */
-                       do_pam_session(pw->pw_name,ttyname);
+                       do_pam_session(pw->pw_name, ttyname);
 #endif /* USE_PAM */
 
                        break;
@@ -1876,7 +1768,7 @@ do_authenticated(struct passwd * pw)
 #endif /* XAUTH_PATH */
 
                case SSH_CMSG_AGENT_REQUEST_FORWARDING:
-                       if (no_agent_forwarding_flag) {
+                       if (no_agent_forwarding_flag || compat13) {
                                debug("Authentication agent forwarding not permitted for this authentication.");
                                goto fail;
                        }
@@ -1903,6 +1795,9 @@ do_authenticated(struct passwd * pw)
                        packet_set_interactive(have_pty || display != NULL,
                                               options.keepalives);
 
+#ifdef USE_PAM
+                       do_pam_setcred();
+#endif /* USE_PAM */
                        if (forced_command != NULL)
                                goto do_forced_command;
                        debug("Forking shell.");
@@ -1918,6 +1813,9 @@ do_authenticated(struct passwd * pw)
                        packet_set_interactive(have_pty || display != NULL,
                                               options.keepalives);
 
+#ifdef USE_PAM
+                       do_pam_setcred();
+#endif /* USE_PAM */
                        if (forced_command != NULL)
                                goto do_forced_command;
                        /* Get command from the packet. */
@@ -2126,8 +2024,8 @@ do_exec_pty(const char *command, int ptyfd, int ttyfd,
        char line[256];
        struct stat st;
        int quiet_login;
-       struct sockaddr_in from;
-       int fromlen;
+       struct sockaddr_storage from;
+       socklen_t fromlen;
        struct pty_cleanup_context cleanup_context;
 
        /* Get remote host name. */
@@ -2188,17 +2086,16 @@ do_exec_pty(const char *command, int ptyfd, int ttyfd,
                }
                /* Record that there was a login on that terminal. */
                record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname,
-                            &from);
+                            (struct sockaddr *)&from);
 
                /* Check if .hushlogin exists. */
                snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir);
                quiet_login = stat(line, &st) >= 0;
 
 #ifdef USE_PAM
-               /* output the results of the pamconv() */
-               if (!quiet_login && pamconv_msg != NULL)
-                       fprintf(stderr, pamconv_msg);
-#endif
+               if (!quiet_login)
+                       print_pam_messages();
+#endif /* USE_PAM */
 
                /*
                 * If the user has logged in before, display the time of last
@@ -2363,6 +2260,39 @@ read_environment_file(char ***env, unsigned int *envsize,
        fclose(f);
 }
 
+#ifdef USE_PAM
+/*
+ * Sets any environment variables which have been specified by PAM
+ */
+void do_pam_environment(char ***env, int *envsize)
+{
+       char *equals, var_name[512], var_val[512];
+       char **pam_env;
+       int i;
+
+       if ((pam_env = fetch_pam_environment()) == NULL)
+               return;
+       
+       for(i = 0; pam_env[i] != NULL; i++) {
+               if ((equals = strstr(pam_env[i], "=")) == NULL)
+                       continue;
+                       
+               if (strlen(pam_env[i]) < (sizeof(var_name) - 1))
+               {
+                       memset(var_name, '\0', sizeof(var_name));
+                       memset(var_val, '\0', sizeof(var_val));
+
+                       strncpy(var_name, pam_env[i], equals - pam_env[i]);
+                       strcpy(var_val, equals + 1);
+
+                       debug("PAM environment: %s=%s", var_name, var_val);
+
+                       child_set_env(env, envsize, var_name, var_val);
+               }
+       }
+}
+#endif /* USE_PAM */
+
 /*
  * Performs common processing for the child, such as setting up the
  * environment, closing extra file descriptors, setting the user and group
@@ -2395,11 +2325,9 @@ do_child(const char *command, struct passwd * pw, const char *term,
        }
 #endif /* USE_PAM */
 
-#ifdef HAVE_SETLOGIN
        /* Set login name in the kernel. */
        if (setlogin(pw->pw_name) < 0)
                error("setlogin failed: %s", strerror(errno));
-#endif /* HAVE_SETLOGIN */
 
        /* Set uid, gid, and groups. */
        /* Login(1) does this as well, and it needs uid 0 for the "-h"
@@ -2479,7 +2407,7 @@ do_child(const char *command, struct passwd * pw, const char *term,
        }
 
        snprintf(buf, sizeof buf, "%.50s %d %d",
-                get_remote_ipaddr(), get_remote_port(), options.port);
+                get_remote_ipaddr(), get_remote_port(), get_local_port());
        child_set_env(&env, &envsize, "SSH_CLIENT", buf);
 
        if (ttyname)
@@ -2500,23 +2428,7 @@ do_child(const char *command, struct passwd * pw, const char *term,
 
 #ifdef USE_PAM
        /* Pull in any environment variables that may have been set by PAM. */
-       {
-               char *equals, var_name[512], var_val[512];
-               char **pam_env = pam_getenvlist((pam_handle_t *)pamh);
-               int i;
-               for(i = 0; pam_env && pam_env[i]; i++) {
-                       equals = strstr(pam_env[i], "=");
-                       if ((strlen(pam_env[i]) < (sizeof(var_name) - 1)) && (equals != NULL))
-                       {
-                               debug("PAM environment: %s=%s", var_name, var_val);
-                               memset(var_name, '\0', sizeof(var_name));
-                               memset(var_val, '\0', sizeof(var_val));
-                               strncpy(var_name, pam_env[i], equals - pam_env[i]);
-                               strcpy(var_val, equals + 1);
-                               child_set_env(&env, &envsize, var_name, var_val);
-                       }
-               }
-       }
+       do_pam_environment(&env, &envsize);
 #endif /* USE_PAM */
 
        if (xauthfile)
@@ -2562,7 +2474,6 @@ do_child(const char *command, struct passwd * pw, const char *term,
         * descriptors left by system functions.  They will be closed later.
         */
        endpwent();
-       endhostent();
 
        /*
         * Close any extra open file descriptors so that we don\'t have them
This page took 0.086765 seconds and 4 git commands to generate.