OSDN Git Service

Map unknown users to shell for Android GCE.
[android-x86/external-openssh.git] / sshd.c
diff --git a/sshd.c b/sshd.c
index 9fcecea..30f8c6f 100644 (file)
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd.c,v 1.385 2011/06/23 09:34:13 djm Exp $ */
+/* $OpenBSD: sshd.c,v 1.444 2015/02/20 22:17:21 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <limits.h>
 
+#ifdef WITH_OPENSSL
 #include <openssl/dh.h>
 #include <openssl/bn.h>
-#include <openssl/md5.h>
 #include <openssl/rand.h>
 #include "openbsd-compat/openssl-compat.h"
+#endif
 
 #ifdef HAVE_SECUREWARE
 #include <sys/security.h>
 #include "packet.h"
 #include "log.h"
 #include "buffer.h"
+#include "misc.h"
 #include "servconf.h"
 #include "uidswap.h"
 #include "compat.h"
 #include "cipher.h"
+#include "digest.h"
 #include "key.h"
 #include "kex.h"
-#include "dh.h"
 #include "myproposal.h"
 #include "authfile.h"
 #include "pathnames.h"
 #include "canohost.h"
 #include "hostfile.h"
 #include "auth.h"
-#include "misc.h"
+#include "authfd.h"
 #include "msg.h"
 #include "dispatch.h"
 #include "channels.h"
 #include "roaming.h"
 #include "ssh-sandbox.h"
 #include "version.h"
-
-#ifdef LIBWRAP
-#include <tcpd.h>
-#include <syslog.h>
-int allow_severity;
-int deny_severity;
-#endif /* LIBWRAP */
+#include "ssherr.h"
 
 #ifndef O_NOCTTY
 #define O_NOCTTY       0
@@ -191,8 +188,9 @@ int num_listen_socks = 0;
 char *client_version_string = NULL;
 char *server_version_string = NULL;
 
-/* for rekeying XXX fixme */
-Kex *xxx_kex;
+/* Daemon's agent connection */
+int auth_sock = -1;
+int have_agent = 0;
 
 /*
  * Any really sensitive data in the application is contained in this
@@ -206,6 +204,7 @@ struct {
        Key     *server_key;            /* ephemeral server key */
        Key     *ssh1_host_key;         /* ssh1 host key */
        Key     **host_keys;            /* all private host keys */
+       Key     **host_pubkeys;         /* all public host keys */
        Key     **host_certificates;    /* all public host certificates */
        int     have_ssh1_key;
        int     have_ssh2_key;
@@ -230,7 +229,7 @@ u_char *session_id2 = NULL;
 u_int session_id2_len = 0;
 
 /* record remote hostname or ip */
-u_int utmp_len = MAXHOSTNAMELEN;
+u_int utmp_len = HOST_NAME_MAX+1;
 
 /* options.max_startup sized array of fd ints */
 int *startup_pipes = NULL;
@@ -239,6 +238,7 @@ int startup_pipe;           /* in child */
 /* variables used for privilege separation */
 int use_privsep = -1;
 struct monitor *pmonitor = NULL;
+int privsep_is_preauth = 1;
 
 /* global authentication context */
 Authctxt *the_authctxt = NULL;
@@ -256,7 +256,9 @@ struct passwd *privsep_pw = NULL;
 void destroy_sensitive_data(void);
 void demote_sensitive_data(void);
 
+#ifdef WITH_SSH1
 static void do_ssh1_kex(void);
+#endif
 static void do_ssh2_kex(void);
 
 /*
@@ -308,6 +310,7 @@ static void
 sighup_restart(void)
 {
        logit("Received SIGHUP; restarting.");
+       platform_pre_restart();
        close_listen_socks();
        close_startup_pipes();
        alarm(0);  /* alarm timer persists across exec */
@@ -358,6 +361,15 @@ grace_alarm_handler(int sig)
        if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0)
                kill(pmonitor->m_pid, SIGALRM);
 
+       /*
+        * Try to kill any processes that we have spawned, E.g. authorized
+        * keys command helpers.
+        */
+       if (getpgid(0) == getpid()) {
+               signal(SIGTERM, SIG_IGN);
+               kill(0, SIGTERM);
+       }
+
        /* Log error and exit. */
        sigdie("Timeout before authentication for %s", get_remote_ipaddr());
 }
@@ -381,7 +393,6 @@ generate_ephemeral_server_key(void)
        verbose("RSA key generation complete.");
 
        arc4random_buf(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH);
-       arc4random_stir();
 }
 
 /*ARGSUSED*/
@@ -418,9 +429,11 @@ sshd_exchange_identification(int sock_in, int sock_out)
                major = PROTOCOL_MAJOR_1;
                minor = PROTOCOL_MINOR_1;
        }
-       snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", major, minor,
-           SSH_VERSION, newline);
-       server_version_string = xstrdup(buf);
+
+       xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s%s",
+           major, minor, SSH_VERSION,
+           *options.version_addendum == '\0' ? "" : " ",
+           options.version_addendum, newline);
 
        /* Send our protocol version identification. */
        if (roaming_atomicio(vwrite, sock_out, server_version_string,
@@ -462,28 +475,36 @@ sshd_exchange_identification(int sock_in, int sock_out)
            &remote_major, &remote_minor, remote_version) != 3) {
                s = "Protocol mismatch.\n";
                (void) atomicio(vwrite, sock_out, s, strlen(s));
+               logit("Bad protocol version identification '%.100s' "
+                   "from %s port %d", client_version_string,
+                   get_remote_ipaddr(), get_remote_port());
                close(sock_in);
                close(sock_out);
-               logit("Bad protocol version identification '%.100s' from %s",
-                   client_version_string, get_remote_ipaddr());
                cleanup_exit(255);
        }
        debug("Client protocol version %d.%d; client software version %.100s",
            remote_major, remote_minor, remote_version);
 
-       compat_datafellows(remote_version);
+       active_state->compat = compat_datafellows(remote_version);
 
-       if (datafellows & SSH_BUG_PROBE) {
+       if ((datafellows & SSH_BUG_PROBE) != 0) {
                logit("probed from %s with %s.  Don't panic.",
                    get_remote_ipaddr(), client_version_string);
                cleanup_exit(255);
        }
-
-       if (datafellows & SSH_BUG_SCANNER) {
+       if ((datafellows & SSH_BUG_SCANNER) != 0) {
                logit("scanned from %s with %s.  Don't panic.",
                    get_remote_ipaddr(), client_version_string);
                cleanup_exit(255);
        }
+       if ((datafellows & SSH_BUG_RSASIGMD5) != 0) {
+               logit("Client version \"%.100s\" uses unsafe RSA signature "
+                   "scheme; disabling use of RSA keys", remote_version);
+       }
+       if ((datafellows & SSH_BUG_DERIVEKEY) != 0) {
+               fatal("Client version \"%.100s\" uses unsafe key agreement; "
+                   "refusing connection", remote_version);
+       }
 
        mismatch = 0;
        switch (remote_major) {
@@ -553,7 +574,7 @@ destroy_sensitive_data(void)
                }
        }
        sensitive_data.ssh1_host_key = NULL;
-       memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH);
+       explicit_bzero(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH);
 }
 
 /* Demote private to public keys for network child */
@@ -592,9 +613,18 @@ privsep_preauth_child(void)
        /* Enable challenge-response authentication for privilege separation */
        privsep_challenge_enable();
 
+#ifdef GSSAPI
+       /* Cache supported mechanism OIDs for later use */
+       if (options.gss_authentication)
+               ssh_gssapi_prepare_supported_oids();
+#endif
+
        arc4random_stir();
        arc4random_buf(rnd, sizeof(rnd));
+#ifdef WITH_OPENSSL
        RAND_seed(rnd, sizeof(rnd));
+#endif
+       explicit_bzero(rnd, sizeof(rnd));
 
        /* Demote the private keys to public keys. */
        demote_sensitive_data();
@@ -623,26 +653,34 @@ privsep_preauth_child(void)
 static int
 privsep_preauth(Authctxt *authctxt)
 {
-       int status;
+       int status, r;
        pid_t pid;
        struct ssh_sandbox *box = NULL;
 
        /* Set up unprivileged child process to deal with network data */
        pmonitor = monitor_init();
        /* Store a pointer to the kex for later rekeying */
-       pmonitor->m_pkex = &xxx_kex;
+       pmonitor->m_pkex = &active_state->kex;
 
-       if (use_privsep == PRIVSEP_SANDBOX)
-               box = ssh_sandbox_init();
+       if (use_privsep == PRIVSEP_ON)
+               box = ssh_sandbox_init(pmonitor);
        pid = fork();
        if (pid == -1) {
                fatal("fork of unprivileged child failed");
        } else if (pid != 0) {
                debug2("Network child is on pid %ld", (long)pid);
 
+               pmonitor->m_pid = pid;
+               if (have_agent) {
+                       r = ssh_get_authentication_socket(&auth_sock);
+                       if (r != 0) {
+                               error("Could not get agent socket: %s",
+                                   ssh_err(r));
+                               have_agent = 0;
+                       }
+               }
                if (box != NULL)
                        ssh_sandbox_parent_preauth(box, pid);
-               pmonitor->m_pid = pid;
                monitor_child_preauth(authctxt, pmonitor);
 
                /* Sync memory */
@@ -650,10 +688,13 @@ privsep_preauth(Authctxt *authctxt)
 
                /* Wait for the child's exit status */
                while (waitpid(pid, &status, 0) < 0) {
-                       if (errno != EINTR)
-                               fatal("%s: waitpid: %s", __func__,
-                                   strerror(errno));
+                       if (errno == EINTR)
+                               continue;
+                       pmonitor->m_pid = -1;
+                       fatal("%s: waitpid: %s", __func__, strerror(errno));
                }
+               privsep_is_preauth = 0;
+               pmonitor->m_pid = -1;
                if (WIFEXITED(status)) {
                        if (WEXITSTATUS(status) != 0)
                                fatal("%s: preauth child exited with status %d",
@@ -723,7 +764,10 @@ privsep_postauth(Authctxt *authctxt)
 
        arc4random_stir();
        arc4random_buf(rnd, sizeof(rnd));
+#ifdef WITH_OPENSSL
        RAND_seed(rnd, sizeof(rnd));
+#endif
+       explicit_bzero(rnd, sizeof(rnd));
 
        /* Drop privileges */
        do_setusercontext(authctxt->pw);
@@ -752,11 +796,14 @@ list_hostkey_types(void)
        for (i = 0; i < options.num_host_key_files; i++) {
                key = sensitive_data.host_keys[i];
                if (key == NULL)
+                       key = sensitive_data.host_pubkeys[i];
+               if (key == NULL)
                        continue;
                switch (key->type) {
                case KEY_RSA:
                case KEY_DSA:
                case KEY_ECDSA:
+               case KEY_ED25519:
                        if (buffer_len(&b) > 0)
                                buffer_append(&b, ",", 1);
                        p = key_ssh_name(key);
@@ -773,6 +820,7 @@ list_hostkey_types(void)
                case KEY_RSA_CERT:
                case KEY_DSA_CERT:
                case KEY_ECDSA_CERT:
+               case KEY_ED25519_CERT:
                        if (buffer_len(&b) > 0)
                                buffer_append(&b, ",", 1);
                        p = key_ssh_name(key);
@@ -788,7 +836,7 @@ list_hostkey_types(void)
 }
 
 static Key *
-get_hostkey_by_type(int type, int need_private)
+get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
 {
        int i;
        Key *key;
@@ -800,13 +848,17 @@ get_hostkey_by_type(int type, int need_private)
                case KEY_RSA_CERT:
                case KEY_DSA_CERT:
                case KEY_ECDSA_CERT:
+               case KEY_ED25519_CERT:
                        key = sensitive_data.host_certificates[i];
                        break;
                default:
                        key = sensitive_data.host_keys[i];
+                       if (key == NULL && !need_private)
+                               key = sensitive_data.host_pubkeys[i];
                        break;
                }
-               if (key != NULL && key->type == type)
+               if (key != NULL && key->type == type &&
+                   (key->type != KEY_ECDSA || key->ecdsa_nid == nid))
                        return need_private ?
                            sensitive_data.host_keys[i] : key;
        }
@@ -814,15 +866,15 @@ get_hostkey_by_type(int type, int need_private)
 }
 
 Key *
-get_hostkey_public_by_type(int type)
+get_hostkey_public_by_type(int type, int nid, struct ssh *ssh)
 {
-       return get_hostkey_by_type(type, 0);
+       return get_hostkey_by_type(type, nid, 0, ssh);
 }
 
 Key *
-get_hostkey_private_by_type(int type)
+get_hostkey_private_by_type(int type, int nid, struct ssh *ssh)
 {
-       return get_hostkey_by_type(type, 1);
+       return get_hostkey_by_type(type, nid, 1, ssh);
 }
 
 Key *
@@ -833,23 +885,80 @@ get_hostkey_by_index(int ind)
        return (sensitive_data.host_keys[ind]);
 }
 
+Key *
+get_hostkey_public_by_index(int ind, struct ssh *ssh)
+{
+       if (ind < 0 || ind >= options.num_host_key_files)
+               return (NULL);
+       return (sensitive_data.host_pubkeys[ind]);
+}
+
 int
-get_hostkey_index(Key *key)
+get_hostkey_index(Key *key, int compare, struct ssh *ssh)
 {
        int i;
 
        for (i = 0; i < options.num_host_key_files; i++) {
                if (key_is_cert(key)) {
-                       if (key == sensitive_data.host_certificates[i])
+                       if (key == sensitive_data.host_certificates[i] ||
+                           (compare && sensitive_data.host_certificates[i] &&
+                           sshkey_equal(key,
+                           sensitive_data.host_certificates[i])))
                                return (i);
                } else {
-                       if (key == sensitive_data.host_keys[i])
+                       if (key == sensitive_data.host_keys[i] ||
+                           (compare && sensitive_data.host_keys[i] &&
+                           sshkey_equal(key, sensitive_data.host_keys[i])))
+                               return (i);
+                       if (key == sensitive_data.host_pubkeys[i] ||
+                           (compare && sensitive_data.host_pubkeys[i] &&
+                           sshkey_equal(key, sensitive_data.host_pubkeys[i])))
                                return (i);
                }
        }
        return (-1);
 }
 
+/* Inform the client of all hostkeys */
+static void
+notify_hostkeys(struct ssh *ssh)
+{
+       struct sshbuf *buf;
+       struct sshkey *key;
+       int i, nkeys, r;
+       char *fp;
+
+       if ((buf = sshbuf_new()) == NULL)
+               fatal("%s: sshbuf_new", __func__);
+       for (i = nkeys = 0; i < options.num_host_key_files; i++) {
+               key = get_hostkey_public_by_index(i, ssh);
+               if (key == NULL || key->type == KEY_UNSPEC ||
+                   key->type == KEY_RSA1 || sshkey_is_cert(key))
+                       continue;
+               fp = sshkey_fingerprint(key, options.fingerprint_hash,
+                   SSH_FP_DEFAULT);
+               debug3("%s: key %d: %s %s", __func__, i,
+                   sshkey_ssh_name(key), fp);
+               free(fp);
+               if (nkeys == 0) {
+                       packet_start(SSH2_MSG_GLOBAL_REQUEST);
+                       packet_put_cstring("hostkeys-00@openssh.com");
+                       packet_put_char(0); /* want-reply */
+               }
+               sshbuf_reset(buf);
+               if ((r = sshkey_putb(key, buf)) != 0)
+                       fatal("%s: couldn't put hostkey %d: %s",
+                           __func__, i, ssh_err(r));
+               packet_put_string(sshbuf_ptr(buf), sshbuf_len(buf));
+               nkeys++;
+       }
+       debug3("%s: sent %d hostkeys", __func__, nkeys);
+       if (nkeys == 0)
+               fatal("%s: no hostkeys", __func__);
+       packet_send();
+       sshbuf_free(buf);
+}
+
 /*
  * returns 1 if connection should be dropped, 0 otherwise.
  * dropping starts at connection #max_startups_begin with a probability
@@ -882,11 +991,18 @@ static void
 usage(void)
 {
        fprintf(stderr, "%s, %s\n",
-           SSH_RELEASE, SSLeay_version(SSLEAY_VERSION));
+           SSH_RELEASE,
+#ifdef WITH_OPENSSL
+           SSLeay_version(SSLEAY_VERSION)
+#else
+           "without OpenSSL"
+#endif
+       );
        fprintf(stderr,
 "usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_cert_file]\n"
-"            [-f config_file] [-g login_grace_time] [-h host_key_file]\n"
-"            [-k key_gen_time] [-o option] [-p port] [-u len]\n"
+"            [-E log_file] [-f config_file] [-g login_grace_time]\n"
+"            [-h host_key_file] [-k key_gen_time] [-o option] [-p port]\n"
+"            [-u len]\n"
        );
        exit(1);
 }
@@ -914,6 +1030,7 @@ send_rexec_state(int fd, Buffer *conf)
        buffer_init(&m);
        buffer_put_cstring(&m, buffer_ptr(conf));
 
+#ifdef WITH_SSH1
        if (sensitive_data.server_key != NULL &&
            sensitive_data.server_key->type == KEY_RSA1) {
                buffer_put_int(&m, 1);
@@ -924,9 +1041,10 @@ send_rexec_state(int fd, Buffer *conf)
                buffer_put_bignum(&m, sensitive_data.server_key->rsa->p);
                buffer_put_bignum(&m, sensitive_data.server_key->rsa->q);
        } else
+#endif
                buffer_put_int(&m, 0);
 
-#ifndef OPENSSL_PRNG_ONLY
+#if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY)
        rexec_send_rng_seed(&m);
 #endif
 
@@ -957,9 +1075,10 @@ recv_rexec_state(int fd, Buffer *conf)
        cp = buffer_get_string(&m, &len);
        if (conf != NULL)
                buffer_append(conf, cp, len + 1);
-       xfree(cp);
+       free(cp);
 
        if (buffer_get_int(&m)) {
+#ifdef WITH_SSH1
                if (sensitive_data.server_key != NULL)
                        key_free(sensitive_data.server_key);
                sensitive_data.server_key = key_new_private(KEY_RSA1);
@@ -969,11 +1088,16 @@ recv_rexec_state(int fd, Buffer *conf)
                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);
+               if (rsa_generate_additional_parameters(
+                   sensitive_data.server_key->rsa) != 0)
+                       fatal("%s: rsa_generate_additional_parameters "
+                           "error", __func__);
+#else
+               fatal("ssh1 not supported");
+#endif
        }
 
-#ifndef OPENSSL_PRNG_ONLY
+#if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY)
        rexec_recv_rng_seed(&m);
 #endif
 
@@ -1008,7 +1132,9 @@ server_accept_inetd(int *sock_in, int *sock_out)
        if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
                dup2(fd, STDIN_FILENO);
                dup2(fd, STDOUT_FILENO);
-               if (fd > STDOUT_FILENO)
+               if (!log_stderr)
+                       dup2(fd, STDERR_FILENO);
+               if (fd > (log_stderr ? STDERR_FILENO : STDOUT_FILENO))
                        close(fd);
        }
        debug("inetd sockets after dupping: %d, %d", *sock_in, *sock_out);
@@ -1099,6 +1225,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
        struct sockaddr_storage from;
        socklen_t fromlen;
        pid_t pid;
+       u_char rnd[256];
 
        /* setup fd set for accept */
        fdset = NULL;
@@ -1119,7 +1246,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
                if (received_sighup)
                        sighup_restart();
                if (fdset != NULL)
-                       xfree(fdset);
+                       free(fdset);
                fdset = (fd_set *)xcalloc(howmany(maxfd + 1, NFDBITS),
                    sizeof(fd_mask));
 
@@ -1137,7 +1264,8 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
                        logit("Received signal %d; terminating.",
                            (int) received_sigterm);
                        close_listen_socks();
-                       unlink(options.pid_file);
+                       if (options.pid_file != NULL)
+                               unlink(options.pid_file);
                        exit(received_sigterm == SIGTERM ? 0 : 255);
                }
                if (key_used && key_do_regen) {
@@ -1168,9 +1296,12 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
                        *newsock = accept(listen_socks[i],
                            (struct sockaddr *)&from, &fromlen);
                        if (*newsock < 0) {
-                               if (errno != EINTR && errno != EAGAIN &&
-                                   errno != EWOULDBLOCK)
-                                       error("accept: %.100s", strerror(errno));
+                               if (errno != EINTR && errno != EWOULDBLOCK &&
+                                   errno != ECONNABORTED && errno != EAGAIN)
+                                       error("accept: %.100s",
+                                           strerror(errno));
+                               if (errno == EMFILE || errno == ENFILE)
+                                       usleep(100 * 1000);
                                continue;
                        }
                        if (unset_nonblock(*newsock) == -1) {
@@ -1296,6 +1427,11 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
                         * from that of the child
                         */
                        arc4random_stir();
+                       arc4random_buf(rnd, sizeof(rnd));
+#ifdef WITH_OPENSSL
+                       RAND_seed(rnd, sizeof(rnd));
+#endif
+                       explicit_bzero(rnd, sizeof(rnd));
                }
 
                /* child process check (or debug mode) */
@@ -1313,17 +1449,20 @@ main(int ac, char **av)
 {
        extern char *optarg;
        extern int optind;
-       int opt, i, j, on = 1;
+       int r, opt, i, j, on = 1;
        int sock_in = -1, sock_out = -1, newsock = -1;
        const char *remote_ip;
-       char *test_user = NULL, *test_host = NULL, *test_addr = NULL;
        int remote_port;
-       char *line, *p, *cp;
+       char *fp, *line, *logfile = NULL;
        int config_s[2] = { -1 , -1 };
+       u_int n;
        u_int64_t ibytes, obytes;
        mode_t new_umask;
        Key *key;
+       Key *pubkey;
+       int keytype;
        Authctxt *authctxt;
+       struct connection_info *connection_info = get_connection_info(0, 0);
 
 #ifdef HAVE_SECUREWARE
        (void)set_auth_parameters(ac, av);
@@ -1354,7 +1493,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:C:dDeiqrtQRT46")) != -1) {
+       while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:C:dDeE:iqrtQRT46")) != -1) {
                switch (opt) {
                case '4':
                        options.address_family = AF_INET;
@@ -1383,6 +1522,9 @@ main(int ac, char **av)
                case 'D':
                        no_daemon_flag = 1;
                        break;
+               case 'E':
+                       logfile = xstrdup(optarg);
+                       /* FALLTHROUGH */
                case 'e':
                        log_stderr = 1;
                        break;
@@ -1445,24 +1587,13 @@ main(int ac, char **av)
                        test_flag = 2;
                        break;
                case 'C':
-                       cp = optarg;
-                       while ((p = strsep(&cp, ",")) && *p != '\0') {
-                               if (strncmp(p, "addr=", 5) == 0)
-                                       test_addr = xstrdup(p + 5);
-                               else if (strncmp(p, "host=", 5) == 0)
-                                       test_host = xstrdup(p + 5);
-                               else if (strncmp(p, "user=", 5) == 0)
-                                       test_user = xstrdup(p + 5);
-                               else {
-                                       fprintf(stderr, "Invalid test "
-                                           "mode specification %s\n", p);
-                                       exit(1);
-                               }
-                       }
+                       if (parse_server_match_testspec(connection_info,
+                           optarg) == -1)
+                               exit(1);
                        break;
                case 'u':
-                       utmp_len = (u_int)strtonum(optarg, 0, MAXHOSTNAMELEN+1, NULL);
-                       if (utmp_len > MAXHOSTNAMELEN) {
+                       utmp_len = (u_int)strtonum(optarg, 0, HOST_NAME_MAX+1+1, NULL);
+                       if (utmp_len > HOST_NAME_MAX+1) {
                                fprintf(stderr, "Invalid utmp length.\n");
                                exit(1);
                        }
@@ -1470,9 +1601,9 @@ main(int ac, char **av)
                case 'o':
                        line = xstrdup(optarg);
                        if (process_server_config_line(&options, line,
-                           "command-line", 0, NULL, NULL, NULL, NULL) != 0)
+                           "command-line", 0, NULL, NULL) != 0)
                                exit(1);
-                       xfree(line);
+                       free(line);
                        break;
                case '?':
                default:
@@ -1489,8 +1620,15 @@ main(int ac, char **av)
        else
                closefrom(REEXEC_DEVCRYPTO_RESERVED_FD);
 
+#ifdef WITH_OPENSSL
        OpenSSL_add_all_algorithms();
+#endif
 
+       /* If requested, redirect the logs to the specified logfile. */
+       if (logfile != NULL) {
+               log_redirect_stderr_to(logfile);
+               free(logfile);
+       }
        /*
         * Force logging to stderr until we have loaded the private host
         * key (unless started from inetd)
@@ -1507,7 +1645,7 @@ main(int ac, char **av)
         * root's environment
         */
        if (getenv("KRB5CCNAME") != NULL)
-               unsetenv("KRB5CCNAME");
+               (void) unsetenv("KRB5CCNAME");
 
 #ifdef _UNICOS
        /* Cray can define user privs drop all privs now!
@@ -1526,13 +1664,10 @@ main(int ac, char **av)
         * the parameters we need.  If we're not doing an extended test,
         * do not silently ignore connection test params.
         */
-       if (test_flag >= 2 &&
-          (test_user != NULL || test_host != NULL || test_addr != NULL)
-           && (test_user == NULL || test_host == NULL || test_addr == NULL))
+       if (test_flag >= 2 && server_match_spec_complete(connection_info) == 0)
                fatal("user, host and addr are all required when testing "
                   "Match configs");
-       if (test_flag < 2 && (test_user != NULL || test_host != NULL ||
-           test_addr != NULL))
+       if (test_flag < 2 && server_match_spec_complete(connection_info) >= 0)
                fatal("Config test connection parameter (-C) provided without "
                   "test mode (-T)");
 
@@ -1544,7 +1679,7 @@ main(int ac, char **av)
                load_server_config(config_file_name, &cfg);
 
        parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name,
-           &cfg, NULL, NULL, NULL);
+           &cfg, NULL);
 
        seed_rng();
 
@@ -1555,6 +1690,33 @@ main(int ac, char **av)
        if (options.challenge_response_authentication)
                options.kbd_interactive_authentication = 1;
 
+       /* Check that options are sensible */
+       if (options.authorized_keys_command_user == NULL &&
+           (options.authorized_keys_command != NULL &&
+           strcasecmp(options.authorized_keys_command, "none") != 0))
+               fatal("AuthorizedKeysCommand set without "
+                   "AuthorizedKeysCommandUser");
+
+       /*
+        * Check whether there is any path through configured auth methods.
+        * Unfortunately it is not possible to verify this generally before
+        * daemonisation in the presence of Match block, but this catches
+        * and warns for trivial misconfigurations that could break login.
+        */
+       if (options.num_auth_methods != 0) {
+               if ((options.protocol & SSH_PROTO_1))
+                       fatal("AuthenticationMethods is not supported with "
+                           "SSH protocol 1");
+               for (n = 0; n < options.num_auth_methods; n++) {
+                       if (auth2_methods_valid(options.auth_methods[n],
+                           1) == 0)
+                               break;
+               }
+               if (n >= options.num_auth_methods)
+                       fatal("AuthenticationMethods cannot be satisfied by "
+                           "enabled authentication methods");
+       }
+
        /* set default channel AF */
        channel_set_af(options.address_family);
 
@@ -1564,7 +1726,13 @@ main(int ac, char **av)
                exit(1);
        }
 
-       debug("sshd version %.100s", SSH_RELEASE);
+       debug("sshd version %s, %s", SSH_VERSION,
+#ifdef WITH_OPENSSL
+           SSLeay_version(SSLEAY_VERSION)
+#else
+           "without OpenSSL"
+#endif
+       );
 
        /* Store privilege separation user for later use if required. */
        if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) {
@@ -1572,31 +1740,63 @@ main(int ac, char **av)
                        fatal("Privilege separation user %s does not exist",
                            SSH_PRIVSEP_USER);
        } else {
-               if (privsep_pw->pw_passwd)
-                       memset(privsep_pw->pw_passwd, 0, strlen(privsep_pw->pw_passwd));
+               if (privsep_pw->pw_passwd != NULL) {
+                       explicit_bzero(privsep_pw->pw_passwd,
+                           strlen(privsep_pw->pw_passwd));
+               }
                privsep_pw = pwcopy(privsep_pw);
-               if (privsep_pw->pw_passwd)
-                       xfree(privsep_pw->pw_passwd);
+               if (privsep_pw->pw_passwd != NULL) {
+                       free(privsep_pw->pw_passwd);
+               }
                privsep_pw->pw_passwd = xstrdup("*");
        }
+#if !defined(ANDROID)
        endpwent();
+#endif
 
-       /* load private host keys */
+       /* load host keys */
        sensitive_data.host_keys = xcalloc(options.num_host_key_files,
            sizeof(Key *));
-       for (i = 0; i < options.num_host_key_files; i++)
-               sensitive_data.host_keys[i] = NULL;
+       sensitive_data.host_pubkeys = xcalloc(options.num_host_key_files,
+           sizeof(Key *));
+
+       if (options.host_key_agent) {
+               if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME))
+                       setenv(SSH_AUTHSOCKET_ENV_NAME,
+                           options.host_key_agent, 1);
+               if ((r = ssh_get_authentication_socket(NULL)) == 0)
+                       have_agent = 1;
+               else
+                       error("Could not connect to agent \"%s\": %s",
+                           options.host_key_agent, ssh_err(r));
+       }
 
        for (i = 0; i < options.num_host_key_files; i++) {
+               if (options.host_key_files[i] == NULL)
+                       continue;
                key = key_load_private(options.host_key_files[i], "", NULL);
+               pubkey = key_load_public(options.host_key_files[i], NULL);
+               if (pubkey == NULL && key != NULL)
+                       pubkey = key_demote(key);
                sensitive_data.host_keys[i] = key;
-               if (key == NULL) {
+               sensitive_data.host_pubkeys[i] = pubkey;
+
+               if (key == NULL && pubkey != NULL && pubkey->type != KEY_RSA1 &&
+                   have_agent) {
+                       debug("will rely on agent for hostkey %s",
+                           options.host_key_files[i]);
+                       keytype = pubkey->type;
+               } else if (key != NULL) {
+                       keytype = key->type;
+               } else {
                        error("Could not load host key: %s",
                            options.host_key_files[i]);
                        sensitive_data.host_keys[i] = NULL;
+                       sensitive_data.host_pubkeys[i] = NULL;
                        continue;
                }
-               switch (key->type) {
+
+               switch (keytype) {
                case KEY_RSA1:
                        sensitive_data.ssh1_host_key = key;
                        sensitive_data.have_ssh1_key = 1;
@@ -1604,11 +1804,18 @@ main(int ac, char **av)
                case KEY_RSA:
                case KEY_DSA:
                case KEY_ECDSA:
-                       sensitive_data.have_ssh2_key = 1;
+               case KEY_ED25519:
+                       if (have_agent || key != NULL)
+                               sensitive_data.have_ssh2_key = 1;
                        break;
                }
-               debug("private host key: #%d type %d %s", i, key->type,
-                   key_type(key));
+               if ((fp = sshkey_fingerprint(pubkey, options.fingerprint_hash,
+                   SSH_FP_DEFAULT)) == NULL)
+                       fatal("sshkey_fingerprint failed");
+               debug("%s host key #%d: %s %s",
+                   key ? "private" : "agent", i, keytype == KEY_RSA1 ?
+                   sshkey_type(pubkey) : sshkey_ssh_name(pubkey), fp);
+               free(fp);
        }
        if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) {
                logit("Disabling protocol version 1. Could not load host key");
@@ -1633,6 +1840,8 @@ main(int ac, char **av)
                sensitive_data.host_certificates[i] = NULL;
 
        for (i = 0; i < options.num_host_cert_files; i++) {
+               if (options.host_cert_files[i] == NULL)
+                       continue;
                key = key_load_public(options.host_cert_files[i], NULL);
                if (key == NULL) {
                        error("Could not load host certificate: %s",
@@ -1663,6 +1872,8 @@ main(int ac, char **av)
                debug("host certificate: #%d type %d %s", j, key->type,
                    key_type(key));
        }
+
+#ifdef WITH_SSH1
        /* Check certain values for sanity. */
        if (options.protocol & SSH_PROTO_1) {
                if (options.server_key_bits < 512 ||
@@ -1687,6 +1898,7 @@ main(int ac, char **av)
                            options.server_key_bits);
                }
        }
+#endif
 
        if (use_privsep) {
                struct stat st;
@@ -1708,9 +1920,8 @@ main(int ac, char **av)
        }
 
        if (test_flag > 1) {
-               if (test_user != NULL && test_addr != NULL && test_host != NULL)
-                       parse_server_match_config(&options, test_user,
-                           test_host, test_addr);
+               if (server_match_spec_complete(connection_info) == 1)
+                       parse_server_match_config(&options, connection_info);
                dump_config(&options);
        }
 
@@ -1771,12 +1982,10 @@ main(int ac, char **av)
        /* Reinitialize the log (because of the fork above). */
        log_init(__progname, options.log_level, options.log_facility, log_stderr);
 
-       /* Initialize the random number generator. */
-       arc4random_stir();
-
        /* Chdir to the root directory so that the current disk can be
           unmounted if desired. */
-       chdir("/");
+       if (chdir("/") == -1)
+               error("chdir(\"/\"): %s", strerror(errno));
 
        /* ignore SIGPIPE */
        signal(SIGPIPE, SIG_IGN);
@@ -1800,7 +2009,7 @@ main(int ac, char **av)
                 * Write out the pid file after the sigterm handler
                 * is setup and the listen sockets are bound
                 */
-               if (!debug_flag) {
+               if (options.pid_file != NULL && !debug_flag) {
                        FILE *f = fopen(options.pid_file, "w");
 
                        if (f == NULL) {
@@ -1844,13 +2053,14 @@ main(int ac, char **av)
                dup2(STDIN_FILENO, STDOUT_FILENO);
                if (startup_pipe == -1)
                        close(REEXEC_STARTUP_PIPE_FD);
-               else
+               else if (startup_pipe != REEXEC_STARTUP_PIPE_FD) {
                        dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD);
+                       close(startup_pipe);
+                       startup_pipe = REEXEC_STARTUP_PIPE_FD;
+               }
 
                dup2(config_s[1], REEXEC_CONFIG_PASS_FD);
                close(config_s[1]);
-               if (startup_pipe != -1)
-                       close(startup_pipe);
 
                execv(rexec_argv[0], rexec_argv);
 
@@ -1861,8 +2071,6 @@ main(int ac, char **av)
                    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) {
@@ -1924,27 +2132,11 @@ main(int ac, char **av)
 #ifdef SSH_AUDIT_EVENTS
        audit_connection_from(remote_ip, remote_port);
 #endif
-#ifdef LIBWRAP
-       allow_severity = options.log_facility|LOG_INFO;
-       deny_severity = options.log_facility|LOG_WARNING;
-       /* 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);
-               fromhost(&req);
-
-               if (!hosts_access(&req)) {
-                       debug("Connection refused by tcp wrapper");
-                       refuse(&req);
-                       /* NOTREACHED */
-                       fatal("libwrap refuse returns");
-               }
-       }
-#endif /* LIBWRAP */
 
        /* Log the connection. */
-       verbose("Connection from %.500s port %d", remote_ip, remote_port);
+       verbose("Connection from %s port %d on %s port %d",
+           remote_ip, remote_port,
+           get_local_ipaddr(sock_in), get_local_port());
 
        /*
         * We don't want to listen forever unless the other side
@@ -1978,9 +2170,15 @@ main(int ac, char **av)
        buffer_init(&loginmsg);
        auth_debug_reset();
 
-       if (use_privsep)
+       if (use_privsep) {
                if (privsep_preauth(authctxt) == 1)
                        goto authenticated;
+       } else if (compat20 && have_agent) {
+               if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) {
+                       error("Unable to get agent socket: %s", ssh_err(r));
+                       have_agent = 0;
+               }
+       }
 
        /* perform the key exchange */
        /* authenticate user and start session */
@@ -1988,8 +2186,12 @@ main(int ac, char **av)
                do_ssh2_kex();
                do_authentication2(authctxt);
        } else {
+#ifdef WITH_SSH1
                do_ssh1_kex();
                do_authentication(authctxt);
+#else
+               fatal("ssh1 not supported");
+#endif
        }
        /*
         * If we use privilege separation, the unprivileged child transfers
@@ -2045,12 +2247,15 @@ main(int ac, char **av)
        packet_set_timeout(options.client_alive_interval,
            options.client_alive_count_max);
 
+       /* Try to send all our hostkeys to the client */
+       if (compat20)
+               notify_hostkeys(active_state);
+
        /* Start session. */
        do_authenticated(authctxt);
 
        /* The connection has been terminated. */
-       packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes);
-       packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes);
+       packet_get_bytes(&ibytes, &obytes);
        verbose("Transferred: sent %llu, received %llu bytes",
            (unsigned long long)obytes, (unsigned long long)ibytes);
 
@@ -2073,6 +2278,7 @@ main(int ac, char **av)
        exit(0);
 }
 
+#ifdef WITH_SSH1
 /*
  * Decrypt session_key_int using our private server key and private host key
  * (key with larger modulus first).
@@ -2096,10 +2302,10 @@ ssh1_session_key(BIGNUM *session_key_int)
                            SSH_KEY_BITS_RESERVED);
                }
                if (rsa_private_decrypt(session_key_int, session_key_int,
-                   sensitive_data.server_key->rsa) <= 0)
+                   sensitive_data.server_key->rsa) != 0)
                        rsafail++;
                if (rsa_private_decrypt(session_key_int, session_key_int,
-                   sensitive_data.ssh1_host_key->rsa) <= 0)
+                   sensitive_data.ssh1_host_key->rsa) != 0)
                        rsafail++;
        } else {
                /* Host key has bigger modulus (or they are equal). */
@@ -2114,14 +2320,15 @@ ssh1_session_key(BIGNUM *session_key_int)
                            SSH_KEY_BITS_RESERVED);
                }
                if (rsa_private_decrypt(session_key_int, session_key_int,
-                   sensitive_data.ssh1_host_key->rsa) < 0)
+                   sensitive_data.ssh1_host_key->rsa) != 0)
                        rsafail++;
                if (rsa_private_decrypt(session_key_int, session_key_int,
-                   sensitive_data.server_key->rsa) < 0)
+                   sensitive_data.server_key->rsa) != 0)
                        rsafail++;
        }
        return (rsafail);
 }
+
 /*
  * SSH1 key exchange
  */
@@ -2130,8 +2337,10 @@ do_ssh1_kex(void)
 {
        int i, len;
        int rsafail = 0;
-       BIGNUM *session_key_int;
+       BIGNUM *session_key_int, *fake_key_int, *real_key_int;
        u_char session_key[SSH_SESSION_KEY_LENGTH];
+       u_char fake_key_bytes[4096 / 8];
+       size_t fake_key_len;
        u_char cookie[8];
        u_int cipher_type, auth_mask, protocol_flags;
 
@@ -2209,68 +2418,61 @@ do_ssh1_kex(void)
        debug("Encryption type: %.200s", cipher_name(cipher_type));
 
        /* Get the encrypted integer. */
-       if ((session_key_int = BN_new()) == NULL)
+       if ((real_key_int = BN_new()) == NULL)
                fatal("do_ssh1_kex: BN_new failed");
-       packet_get_bignum(session_key_int);
+       packet_get_bignum(real_key_int);
 
        protocol_flags = packet_get_int();
        packet_set_protocol_flags(protocol_flags);
        packet_check_eom();
 
-       /* Decrypt session_key_int using host/server keys */
-       rsafail = PRIVSEP(ssh1_session_key(session_key_int));
+       /* Setup a fake key in case RSA decryption fails */
+       if ((fake_key_int = BN_new()) == NULL)
+               fatal("do_ssh1_kex: BN_new failed");
+       fake_key_len = BN_num_bytes(real_key_int);
+       if (fake_key_len > sizeof(fake_key_bytes))
+               fake_key_len = sizeof(fake_key_bytes);
+       arc4random_buf(fake_key_bytes, fake_key_len);
+       if (BN_bin2bn(fake_key_bytes, fake_key_len, fake_key_int) == NULL)
+               fatal("do_ssh1_kex: BN_bin2bn failed");
+
+       /* Decrypt real_key_int using host/server keys */
+       rsafail = PRIVSEP(ssh1_session_key(real_key_int));
+       /* If decryption failed, use the fake key. Else, the real key. */
+       if (rsafail)
+               session_key_int = fake_key_int;
+       else
+               session_key_int = real_key_int;
 
        /*
         * Extract session key from the decrypted integer.  The key is in the
         * least significant 256 bits of the integer; the first byte of the
         * key is in the highest bits.
         */
-       if (!rsafail) {
-               (void) BN_mask_bits(session_key_int, sizeof(session_key) * 8);
-               len = BN_num_bytes(session_key_int);
-               if (len < 0 || (u_int)len > sizeof(session_key)) {
-                       error("do_ssh1_kex: bad session key len from %s: "
-                           "session_key_int %d > sizeof(session_key) %lu",
-                           get_remote_ipaddr(), len, (u_long)sizeof(session_key));
-                       rsafail++;
-               } else {
-                       memset(session_key, 0, sizeof(session_key));
-                       BN_bn2bin(session_key_int,
-                           session_key + sizeof(session_key) - len);
-
-                       derive_ssh1_session_id(
-                           sensitive_data.ssh1_host_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.
-                        */
-                       for (i = 0; i < 16; i++)
-                               session_key[i] ^= session_id[i];
-               }
-       }
-       if (rsafail) {
-               int bytes = BN_num_bytes(session_key_int);
-               u_char *buf = xmalloc(bytes);
-               MD5_CTX md;
-
-               logit("do_connection: generating a fake encryption key");
-               BN_bn2bin(session_key_int, buf);
-               MD5_Init(&md);
-               MD5_Update(&md, buf, bytes);
-               MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH);
-               MD5_Final(session_key, &md);
-               MD5_Init(&md);
-               MD5_Update(&md, session_key, 16);
-               MD5_Update(&md, buf, bytes);
-               MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH);
-               MD5_Final(session_key + 16, &md);
-               memset(buf, 0, bytes);
-               xfree(buf);
+       (void) BN_mask_bits(session_key_int, sizeof(session_key) * 8);
+       len = BN_num_bytes(session_key_int);
+       if (len < 0 || (u_int)len > sizeof(session_key)) {
+               error("do_ssh1_kex: bad session key len from %s: "
+                   "session_key_int %d > sizeof(session_key) %lu",
+                   get_remote_ipaddr(), len, (u_long)sizeof(session_key));
+               rsafail++;
+       } else {
+               explicit_bzero(session_key, sizeof(session_key));
+               BN_bn2bin(session_key_int,
+                   session_key + sizeof(session_key) - len);
+
+               derive_ssh1_session_id(
+                   sensitive_data.ssh1_host_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.
+                */
                for (i = 0; i < 16; i++)
-                       session_id[i] = session_key[i] ^ session_key[i + 16];
+                       session_key[i] ^= session_id[i];
        }
+
        /* Destroy the private and public keys. No longer. */
        destroy_sensitive_data();
 
@@ -2278,13 +2480,14 @@ do_ssh1_kex(void)
                mm_ssh1_session_id(session_id);
 
        /* Destroy the decrypted integer.  It is no longer needed. */
-       BN_clear_free(session_key_int);
+       BN_clear_free(real_key_int);
+       BN_clear_free(fake_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);
 
        /* Destroy our copy of the session key.  It is no longer needed. */
-       memset(session_key, 0, sizeof(session_key));
+       explicit_bzero(session_key, sizeof(session_key));
 
        debug("Received session key; encryption turned on.");
 
@@ -2293,6 +2496,33 @@ do_ssh1_kex(void)
        packet_send();
        packet_write_wait();
 }
+#endif
+
+int
+sshd_hostkey_sign(Key *privkey, Key *pubkey, u_char **signature, size_t *slen,
+    const u_char *data, size_t dlen, u_int flag)
+{
+       int r;
+       u_int xxx_slen, xxx_dlen = dlen;
+
+       if (privkey) {
+               if (PRIVSEP(key_sign(privkey, signature, &xxx_slen, data, xxx_dlen) < 0))
+                       fatal("%s: key_sign failed", __func__);
+               if (slen)
+                       *slen = xxx_slen;
+       } else if (use_privsep) {
+               if (mm_key_sign(pubkey, signature, &xxx_slen, data, xxx_dlen) < 0)
+                       fatal("%s: pubkey_sign failed", __func__);
+               if (slen)
+                       *slen = xxx_slen;
+       } else {
+               if ((r = ssh_agent_sign(auth_sock, pubkey, signature, slen,
+                   data, dlen, datafellows)) != 0)
+                       fatal("%s: ssh_agent_sign failed: %s",
+                           __func__, ssh_err(r));
+       }
+       return 0;
+}
 
 /*
  * SSH2 key exchange: diffie-hellman-group1-sha1
@@ -2300,7 +2530,9 @@ do_ssh1_kex(void)
 static void
 do_ssh2_kex(void)
 {
-       Kex *kex;
+       char *myproposal[PROPOSAL_MAX] = { KEX_SERVER };
+       struct kex *kex;
+       int r;
 
        if (options.ciphers != NULL) {
                myproposal[PROPOSAL_ENC_ALGS_CTOS] =
@@ -2325,25 +2557,39 @@ do_ssh2_kex(void)
        if (options.kex_algorithms != NULL)
                myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
 
-       myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types();
+       myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(
+           myproposal[PROPOSAL_KEX_ALGS]);
+
+       if (options.rekey_limit || options.rekey_interval)
+               packet_set_rekey_limits((u_int32_t)options.rekey_limit,
+                   (time_t)options.rekey_interval);
+
+       myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
+           list_hostkey_types());
 
        /* start key exchange */
-       kex = kex_setup(myproposal);
+       if ((r = kex_setup(active_state, myproposal)) != 0)
+               fatal("kex_setup: %s", ssh_err(r));
+       kex = active_state->kex;
+#ifdef WITH_OPENSSL
        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->kex[KEX_DH_GEX_SHA256] = kexgex_server;
+# ifdef OPENSSL_HAS_ECC
        kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
+# endif
+#endif
+       kex->kex[KEX_C25519_SHA256] = kexc25519_server;
        kex->server = 1;
        kex->client_version_string=client_version_string;
        kex->server_version_string=server_version_string;
        kex->load_host_public_key=&get_hostkey_public_by_type;
        kex->load_host_private_key=&get_hostkey_private_by_type;
        kex->host_key_index=&get_hostkey_index;
+       kex->sign = sshd_hostkey_sign;
 
-       xxx_kex = kex;
-
-       dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
+       dispatch_run(DISPATCH_BLOCK, &kex->done, active_state);
 
        session_id2 = kex->session_id;
        session_id2_len = kex->session_id_len;
@@ -2362,8 +2608,17 @@ do_ssh2_kex(void)
 void
 cleanup_exit(int i)
 {
-       if (the_authctxt)
+       if (the_authctxt) {
                do_cleanup(the_authctxt);
+               if (use_privsep && privsep_is_preauth &&
+                   pmonitor != NULL && pmonitor->m_pid > 1) {
+                       debug("Killing privsep child %d", pmonitor->m_pid);
+                       if (kill(pmonitor->m_pid, SIGKILL) != 0 &&
+                           errno != ESRCH)
+                               error("%s: kill(%d): %s", __func__,
+                                   pmonitor->m_pid, strerror(errno));
+               }
+       }
 #ifdef SSH_AUDIT_EVENTS
        /* done after do_cleanup so it can cancel the PAM auth 'thread' */
        if (!use_privsep || mm_is_monitor())