]> andersk Git - openssh.git/blobdiff - sshd.c
- (tim) [configure.ac] updwtmpx() on OpenServer seems to add duplicate entry.
[openssh.git] / sshd.c
diff --git a/sshd.c b/sshd.c
index b9331566b1352be6de6d1065ffc5bbdd1319357b..a9e7ccb3146c1c7b63eb8d35b43e7b8d2e1271b0 100644 (file)
--- a/sshd.c
+++ b/sshd.c
@@ -42,7 +42,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshd.c,v 1.289 2004/03/11 08:36:26 markus Exp $");
+RCSID("$OpenBSD: sshd.c,v 1.299 2004/07/17 05:31:41 dtucker Exp $");
 
 #include <openssl/dh.h>
 #include <openssl/bn.h>
@@ -60,12 +60,12 @@ RCSID("$OpenBSD: sshd.c,v 1.289 2004/03/11 08:36:26 markus Exp $");
 #include "rsa.h"
 #include "sshpty.h"
 #include "packet.h"
-#include "mpaux.h"
 #include "log.h"
 #include "servconf.h"
 #include "uidswap.h"
 #include "compat.h"
 #include "buffer.h"
+#include "bufaux.h"
 #include "cipher.h"
 #include "kex.h"
 #include "key.h"
@@ -77,6 +77,7 @@ RCSID("$OpenBSD: sshd.c,v 1.289 2004/03/11 08:36:26 markus Exp $");
 #include "canohost.h"
 #include "auth.h"
 #include "misc.h"
+#include "msg.h"
 #include "dispatch.h"
 #include "channels.h"
 #include "session.h"
@@ -96,11 +97,13 @@ int deny_severity = LOG_WARNING;
 #define O_NOCTTY       0
 #endif
 
-#ifdef HAVE___PROGNAME
+/* Re-exec fds */
+#define REEXEC_DEVCRYPTO_RESERVED_FD   (STDERR_FILENO + 1)
+#define REEXEC_STARTUP_PIPE_FD         (STDERR_FILENO + 2)
+#define REEXEC_CONFIG_PASS_FD          (STDERR_FILENO + 3)
+#define REEXEC_MIN_FREE_FD             (STDERR_FILENO + 4)
+
 extern char *__progname;
-#else
-char *__progname;
-#endif
 
 /* Server configuration options. */
 ServerOptions options;
@@ -138,6 +141,12 @@ int log_stderr = 0;
 char **saved_argv;
 int saved_argc;
 
+/* re-exec */
+int rexeced_flag = 0;
+int rexec_flag = 1;
+int rexec_argc = 0;
+char **rexec_argv;
+
 /*
  * The sockets that the server is listening; this is used in the SIGHUP
  * signal handler.
@@ -207,6 +216,9 @@ Buffer loginmsg;
 /* global authentication context */
 Authctxt *the_authctxt = NULL;
 
+/* message to be displayed after login */
+Buffer loginmsg;
+
 /* Prototypes for various functions defined later in this file. */
 void destroy_sensitive_data(void);
 void demote_sensitive_data(void);
@@ -763,7 +775,7 @@ drop_connection(int startups)
 static void
 usage(void)
 {
-       fprintf(stderr, "sshd version %s, %s\n",
+       fprintf(stderr, "%s, %s\n",
            SSH_VERSION, SSLeay_version(SSLEAY_VERSION));
        fprintf(stderr,
 "usage: sshd [-46Ddeiqt] [-b bits] [-f config_file] [-g login_grace_time]\n"
@@ -772,6 +784,87 @@ usage(void)
        exit(1);
 }
 
+static void
+send_rexec_state(int fd, Buffer *conf)
+{
+       Buffer m;
+
+       debug3("%s: entering fd = %d config len %d", __func__, fd,
+           buffer_len(conf));
+
+       /*
+        * Protocol from reexec master to child:
+        *      string  configuration
+        *      u_int   ephemeral_key_follows
+        *      bignum  e               (only if ephemeral_key_follows == 1)
+        *      bignum  n                       "
+        *      bignum  d                       "
+        *      bignum  iqmp                    "
+        *      bignum  p                       "
+        *      bignum  q                       "
+        */
+       buffer_init(&m);
+       buffer_put_cstring(&m, buffer_ptr(conf));
+
+       if (sensitive_data.server_key != NULL &&
+           sensitive_data.server_key->type == KEY_RSA1) {
+               buffer_put_int(&m, 1);
+               buffer_put_bignum(&m, sensitive_data.server_key->rsa->e);
+               buffer_put_bignum(&m, sensitive_data.server_key->rsa->n);
+               buffer_put_bignum(&m, sensitive_data.server_key->rsa->d);
+               buffer_put_bignum(&m, sensitive_data.server_key->rsa->iqmp);
+               buffer_put_bignum(&m, sensitive_data.server_key->rsa->p);
+               buffer_put_bignum(&m, sensitive_data.server_key->rsa->q);
+       } else
+               buffer_put_int(&m, 0);
+
+       if (ssh_msg_send(fd, 0, &m) == -1)
+               fatal("%s: ssh_msg_send failed", __func__);
+
+       buffer_free(&m);
+
+       debug3("%s: done", __func__);
+}
+
+static void
+recv_rexec_state(int fd, Buffer *conf)
+{
+       Buffer m;
+       char *cp;
+       u_int len;
+
+       debug3("%s: entering fd = %d", __func__, fd);
+
+       buffer_init(&m);
+
+       if (ssh_msg_recv(fd, &m) == -1)
+               fatal("%s: ssh_msg_recv failed", __func__);
+       if (buffer_get_char(&m) != 0)
+               fatal("%s: rexec version mismatch", __func__);
+
+       cp = buffer_get_string(&m, &len);
+       if (conf != NULL)
+               buffer_append(conf, cp, len + 1);
+       xfree(cp);
+
+       if (buffer_get_int(&m)) {
+               if (sensitive_data.server_key != NULL)
+                       key_free(sensitive_data.server_key);
+               sensitive_data.server_key = key_new_private(KEY_RSA1);
+               buffer_get_bignum(&m, sensitive_data.server_key->rsa->e);
+               buffer_get_bignum(&m, sensitive_data.server_key->rsa->n);
+               buffer_get_bignum(&m, sensitive_data.server_key->rsa->d);
+               buffer_get_bignum(&m, sensitive_data.server_key->rsa->iqmp);
+               buffer_get_bignum(&m, sensitive_data.server_key->rsa->p);
+               buffer_get_bignum(&m, sensitive_data.server_key->rsa->q);
+               rsa_generate_additional_parameters(
+                   sensitive_data.server_key->rsa);
+       }
+       buffer_free(&m);
+
+       debug3("%s: done", __func__);
+}
+
 /*
  * Main program for the daemon.
  */
@@ -780,7 +873,8 @@ main(int ac, char **av)
 {
        extern char *optarg;
        extern int optind;
-       int opt, sock_in = 0, sock_out = 0, newsock, j, i, fdsetsz, on = 1;
+       int opt, j, i, fdsetsz, on = 1;
+       int sock_in = -1, sock_out = -1, newsock = -1;
        pid_t pid;
        socklen_t fromlen;
        fd_set *fdset;
@@ -792,11 +886,12 @@ main(int ac, char **av)
        char ntop[NI_MAXHOST], strport[NI_MAXSERV];
        char *line;
        int listen_sock, maxfd;
-       int startup_p[2];
+       int startup_p[2], config_s[2];
        int startups = 0;
        Key *key;
        Authctxt *authctxt;
        int ret, key_used = 0;
+       Buffer cfg;
 
 #ifdef HAVE_SECUREWARE
        (void)set_auth_parameters(ac, av);
@@ -806,6 +901,7 @@ main(int ac, char **av)
 
        /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */
        saved_argc = ac;
+       rexec_argc = ac;
        saved_argv = xmalloc(sizeof(*saved_argv) * (ac + 1));
        for (i = 0; i < ac; i++)
                saved_argv[i] = xstrdup(av[i]);
@@ -824,7 +920,7 @@ main(int ac, char **av)
        initialize_server_options(&options);
 
        /* Parse command-line arguments. */
-       while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:dDeiqtQ46")) != -1) {
+       while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:dDeiqrtQR46")) != -1) {
                switch (opt) {
                case '4':
                        IPv4or6 = AF_INET;
@@ -851,6 +947,13 @@ main(int ac, char **av)
                case 'i':
                        inetd_flag = 1;
                        break;
+               case 'r':
+                       rexec_flag = 0;
+                       break;
+               case 'R':
+                       rexeced_flag = 1;
+                       inetd_flag = 1;
+                       break;
                case 'Q':
                        /* ignored */
                        break;
@@ -914,6 +1017,15 @@ main(int ac, char **av)
                        break;
                }
        }
+       if (rexeced_flag || inetd_flag)
+               rexec_flag = 0;
+       if (rexec_flag && (av[0] == NULL || *av[0] != '/'))
+               fatal("sshd re-exec requires execution with an absolute path");
+       if (rexeced_flag)
+               closefrom(REEXEC_MIN_FREE_FD);
+       else
+               closefrom(REEXEC_DEVCRYPTO_RESERVED_FD);
+
        SSLeay_add_all_algorithms();
        channel_set_af(IPv4or6);
 
@@ -936,7 +1048,7 @@ main(int ac, char **av)
        unsetenv("KRB5CCNAME");
 #endif /* _AIX */
 #ifdef _UNICOS
-       /* Cray can define user privs drop all prives now!
+       /* Cray can define user privs drop all privs now!
         * Not needed on PRIV_SU systems!
         */
        drop_cray_privs();
@@ -944,8 +1056,23 @@ main(int ac, char **av)
 
        seed_rng();
 
-       /* Read server configuration options from the configuration file. */
-       read_server_config(&options, config_file_name);
+       sensitive_data.server_key = NULL;
+       sensitive_data.ssh1_host_key = NULL;
+       sensitive_data.have_ssh1_key = 0;
+       sensitive_data.have_ssh2_key = 0;
+
+       /* Fetch our configuration */
+       buffer_init(&cfg);
+       if (rexeced_flag)
+               recv_rexec_state(REEXEC_CONFIG_PASS_FD, &cfg);
+       else
+               load_server_config(config_file_name, &cfg);
+
+       parse_server_config(&options,
+           rexeced_flag ? "rexec" : config_file_name, &cfg);
+
+       if (!rexec_flag)
+               buffer_free(&cfg);
 
        /* Fill in default values for those options not explicitly set. */
        fill_default_server_options(&options);
@@ -963,10 +1090,6 @@ main(int ac, char **av)
            sizeof(Key *));
        for (i = 0; i < options.num_host_key_files; i++)
                sensitive_data.host_keys[i] = NULL;
-       sensitive_data.server_key = NULL;
-       sensitive_data.ssh1_host_key = NULL;
-       sensitive_data.have_ssh1_key = 0;
-       sensitive_data.have_ssh2_key = 0;
 
        for (i = 0; i < options.num_host_key_files; i++) {
                key = key_load_private(options.host_key_files[i], "", NULL);
@@ -1065,6 +1188,16 @@ main(int ac, char **av)
        if (setgroups(0, NULL) < 0)
                debug("setgroups() failed: %.200s", strerror(errno));
 
+       if (rexec_flag) {
+               rexec_argv = xmalloc(sizeof(char *) * (rexec_argc + 2));
+               for (i = 0; i < rexec_argc; i++) {
+                       debug("rexec_argv[%d]='%s'", i, saved_argv[i]);
+                       rexec_argv[i] = saved_argv[i];
+               }
+               rexec_argv[rexec_argc] = "-R";
+               rexec_argv[rexec_argc + 1] = NULL;
+       }
+
        /* Initialize the log (it is reinitialized below in case we forked). */
        if (debug_flag && !inetd_flag)
                log_stderr = 1;
@@ -1106,19 +1239,34 @@ main(int ac, char **av)
 
        /* Start listening for a socket, unless started from inetd. */
        if (inetd_flag) {
-               int s1;
-               s1 = dup(0);    /* Make sure descriptors 0, 1, and 2 are in use. */
-               dup(s1);
-               sock_in = dup(0);
-               sock_out = dup(1);
+               int fd;
+
                startup_pipe = -1;
+               if (rexeced_flag) {
+                       close(REEXEC_CONFIG_PASS_FD);
+                       sock_in = sock_out = dup(STDIN_FILENO);
+                       if (!debug_flag) {
+                               startup_pipe = dup(REEXEC_STARTUP_PIPE_FD);
+                               close(REEXEC_STARTUP_PIPE_FD);
+                       }
+               } else {
+                       sock_in = dup(STDIN_FILENO);
+                       sock_out = dup(STDOUT_FILENO);
+               }
                /*
                 * We intentionally do not close the descriptors 0, 1, and 2
-                * as our code for setting the descriptors won\'t work if
+                * as our code for setting the descriptors won't work if
                 * ttyfd happens to be one of those.
                 */
+               if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+                       dup2(fd, STDIN_FILENO);
+                       dup2(fd, STDOUT_FILENO);
+                       if (fd > STDOUT_FILENO)
+                               close(fd);
+               }
                debug("inetd sockets after dupping: %d, %d", sock_in, sock_out);
-               if (options.protocol & SSH_PROTO_1)
+               if ((options.protocol & SSH_PROTO_1) &&
+                   sensitive_data.server_key == NULL)
                        generate_ephemeral_server_key();
        } else {
                for (ai = options.listen_addrs; ai; ai = ai->ai_next) {
@@ -1141,8 +1289,7 @@ main(int ac, char **av)
                                verbose("socket: %.100s", strerror(errno));
                                continue;
                        }
-                       if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) < 0) {
-                               error("listen_sock O_NONBLOCK: %s", strerror(errno));
+                       if (set_nonblock(listen_sock) == -1) {
                                close(listen_sock);
                                continue;
                        }
@@ -1285,8 +1432,7 @@ main(int ac, char **av)
                                                error("accept: %.100s", strerror(errno));
                                        continue;
                                }
-                               if (fcntl(newsock, F_SETFL, 0) < 0) {
-                                       error("newsock del O_NONBLOCK: %s", strerror(errno));
+                               if (unset_nonblock(newsock) == -1) {
                                        close(newsock);
                                        continue;
                                }
@@ -1300,6 +1446,16 @@ main(int ac, char **av)
                                        continue;
                                }
 
+                               if (rexec_flag && socketpair(AF_UNIX,
+                                   SOCK_STREAM, 0, config_s) == -1) {
+                                       error("reexec socketpair: %s",
+                                           strerror(errno));
+                                       close(newsock);
+                                       close(startup_p[0]);
+                                       close(startup_p[1]);
+                                       continue;
+                               }
+
                                for (j = 0; j < options.max_startups; j++)
                                        if (startup_pipes[j] == -1) {
                                                startup_pipes[j] = startup_p[0];
@@ -1323,8 +1479,15 @@ main(int ac, char **av)
                                        close_listen_socks();
                                        sock_in = newsock;
                                        sock_out = newsock;
+                                       close(startup_p[0]);
+                                       close(startup_p[1]);
                                        startup_pipe = -1;
                                        pid = getpid();
+                                       if (rexec_flag) {
+                                               send_rexec_state(config_s[0],
+                                                   &cfg);
+                                               close(config_s[0]);
+                                       }
                                        break;
                                } else {
                                        /*
@@ -1346,6 +1509,7 @@ main(int ac, char **av)
                                                sock_in = newsock;
                                                sock_out = newsock;
                                                log_init(__progname, options.log_level, options.log_facility, log_stderr);
+                                               close(config_s[0]);
                                                break;
                                        }
                                }
@@ -1358,6 +1522,12 @@ main(int ac, char **av)
 
                                close(startup_p[1]);
 
+                               if (rexec_flag) {
+                                       send_rexec_state(config_s[0], &cfg);
+                                       close(config_s[0]);
+                                       close(config_s[1]);
+                               }
+
                                /* Mark that the key has been used (it was "given" to the child). */
                                if ((options.protocol & SSH_PROTO_1) &&
                                    key_used == 0) {
@@ -1381,6 +1551,45 @@ main(int ac, char **av)
        /* This is the child processing a new connection. */
        setproctitle("%s", "[accepted]");
 
+       if (rexec_flag) {
+               int fd;
+
+               debug("rexec start in %d out %d newsock %d pipe %d sock %d",
+                   sock_in, sock_out, newsock, startup_pipe, config_s[0]);
+               dup2(newsock, STDIN_FILENO);
+               dup2(STDIN_FILENO, STDOUT_FILENO);
+               if (startup_pipe == -1)
+                       close(REEXEC_STARTUP_PIPE_FD);
+               else
+                       dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD);
+
+               dup2(config_s[1], REEXEC_CONFIG_PASS_FD);
+               close(config_s[1]);
+               close(startup_pipe);
+
+               execv(rexec_argv[0], rexec_argv);
+
+               /* Reexec has failed, fall back and continue */
+               error("rexec of %s failed: %s", rexec_argv[0], strerror(errno));
+               recv_rexec_state(REEXEC_CONFIG_PASS_FD, NULL);
+               log_init(__progname, options.log_level,
+                   options.log_facility, log_stderr);
+
+               /* Clean up fds */
+               startup_pipe = REEXEC_STARTUP_PIPE_FD;
+               close(config_s[1]);
+               close(REEXEC_CONFIG_PASS_FD);
+               newsock = sock_out = sock_in = dup(STDIN_FILENO);
+               if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+                       dup2(fd, STDIN_FILENO);
+                       dup2(fd, STDOUT_FILENO);
+                       if (fd > STDERR_FILENO)
+                               close(fd);
+               }
+               debug("rexec cleanup in %d out %d newsock %d pipe %d sock %d",
+                   sock_in, sock_out, newsock, startup_pipe, config_s[0]);
+       }
+
        /*
         * Create a new session and process group since the 4.4BSD
         * setlogin() affects the entire process group.  We don't
@@ -1426,7 +1635,7 @@ main(int ac, char **av)
 
 #ifdef LIBWRAP
        /* Check whether logins are denied from this host. */
-       {
+       if (packet_connection_is_on_socket()) {
                struct request_info req;
 
                request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, 0);
@@ -1474,6 +1683,9 @@ main(int ac, char **av)
                if (privsep_preauth(authctxt) == 1)
                        goto authenticated;
 
+       /* prepare buffer to collect messages to display to user after login */
+       buffer_init(&loginmsg);
+
        /* perform the key exchange */
        /* authenticate user and start session */
        if (compat20) {
@@ -1689,9 +1901,10 @@ do_ssh1_kex(void)
                        BN_bn2bin(session_key_int,
                            session_key + sizeof(session_key) - len);
 
-                       compute_session_id(session_id, cookie,
+                       derive_ssh1_session_id(
                            sensitive_data.ssh1_host_key->rsa->n,
-                           sensitive_data.server_key->rsa->n);
+                           sensitive_data.server_key->rsa->n,
+                           cookie, session_id);
                        /*
                         * Xor the first 16 bytes of the session key with the
                         * session id.
@@ -1774,6 +1987,7 @@ do_ssh2_kex(void)
        /* start key exchange */
        kex = kex_setup(myproposal);
        kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
+       kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
        kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
        kex->server = 1;
        kex->client_version_string=client_version_string;
This page took 0.073281 seconds and 4 git commands to generate.