root/crypto/openssh/sshd.c
/* $OpenBSD: sshd.c,v 1.617 2025/04/07 08:12:22 dtucker Exp $ */
/*
 * Copyright (c) 2000, 2001, 2002 Markus Friedl.  All rights reserved.
 * Copyright (c) 2002 Niels Provos.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "includes.h"

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include "openbsd-compat/sys-tree.h"
#include "openbsd-compat/sys-queue.h"
#include <sys/wait.h>
#include <sys/utsname.h>

#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
#include <grp.h>
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#include <pwd.h>
#include <signal.h>
#include <stdarg.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>

#ifdef WITH_OPENSSL
#include <openssl/evp.h>
#include <openssl/rand.h>
#include "openbsd-compat/openssl-compat.h"
#endif

#ifdef HAVE_SECUREWARE
#include <sys/security.h>
#include <prot.h>
#endif

#ifdef __FreeBSD__
#include <resolv.h>
#if defined(GSSAPI) && defined(HAVE_GSSAPI_GSSAPI_H)
#include <gssapi/gssapi.h>
#elif defined(GSSAPI) && defined(HAVE_GSSAPI_H)
#include <gssapi.h>
#endif
#endif

#include "xmalloc.h"
#include "ssh.h"
#include "sshpty.h"
#include "log.h"
#include "sshbuf.h"
#include "misc.h"
#include "servconf.h"
#include "compat.h"
#include "digest.h"
#include "sshkey.h"
#include "authfile.h"
#include "pathnames.h"
#include "canohost.h"
#include "hostfile.h"
#include "auth.h"
#include "authfd.h"
#include "msg.h"
#include "version.h"
#include "ssherr.h"
#include "sk-api.h"
#include "addr.h"
#include "srclimit.h"
#include "atomicio.h"

#ifdef LIBWRAP
#include <tcpd.h>
#include <syslog.h>
#endif /* LIBWRAP */

/* Re-exec fds */
#define REEXEC_DEVCRYPTO_RESERVED_FD    (STDERR_FILENO + 1)
#define REEXEC_CONFIG_PASS_FD           (STDERR_FILENO + 2)
#define REEXEC_MIN_FREE_FD              (STDERR_FILENO + 3)

extern char *__progname;

/* Server configuration options. */
ServerOptions options;

/*
 * 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
 * log, the daemon will not go to background, and will exit after processing
 * the first connection.
 */
int debug_flag = 0;

/* Saved arguments to main(). */
static char **saved_argv;
static int saved_argc;

/*
 * The sockets that the server is listening; this is used in the SIGHUP
 * signal handler.
 */
#define MAX_LISTEN_SOCKS        16
static int listen_socks[MAX_LISTEN_SOCKS];
static int num_listen_socks = 0;

/*
 * Any really sensitive data in the application is contained in this
 * structure. The idea is that this structure could be locked into memory so
 * that the pages do not get written into swap.  However, there are some
 * problems. The private key contains BIGNUMs, and we do not (in principle)
 * have access to the internals of them, and locking just the structure is
 * not very useful.  Currently, memory locking is not implemented.
 */
struct {
        struct sshkey   **host_keys;            /* all private host keys */
        struct sshkey   **host_pubkeys;         /* all public host keys */
        struct sshkey   **host_certificates;    /* all public host certificates */
        int             have_ssh2_key;
} sensitive_data;

/* This is set to true when a signal is received. */
static volatile sig_atomic_t received_siginfo = 0;
static volatile sig_atomic_t received_sigchld = 0;
static volatile sig_atomic_t received_sighup = 0;
static volatile sig_atomic_t received_sigterm = 0;

/* record remote hostname or ip */
u_int utmp_len = HOST_NAME_MAX+1;

/*
 * The early_child/children array below is used for tracking children of the
 * listening sshd process early in their lifespans, before they have
 * completed authentication. This tracking is needed for four things:
 *
 * 1) Implementing the MaxStartups limit of concurrent unauthenticated
 *    connections.
 * 2) Avoiding a race condition for SIGHUP processing, where child processes
 *    may have listen_socks open that could collide with main listener process
 *    after it restarts.
 * 3) Ensuring that rexec'd sshd processes have received their initial state
 *    from the parent listen process before handling SIGHUP.
 * 4) Tracking and logging unsuccessful exits from the preauth sshd monitor,
 *    including and especially those for LoginGraceTime timeouts.
 *
 * Child processes signal that they have completed closure of the listen_socks
 * and (if applicable) received their rexec state by sending a char over their
 * sock.
 *
 * Child processes signal that authentication has completed by sending a
 * second char over the socket before closing it, otherwise the listener will
 * continue tracking the child (and using up a MaxStartups slot) until the
 * preauth subprocess exits, whereupon the listener will log its exit status.
 * preauth processes will exit with a status of EXIT_LOGIN_GRACE to indicate
 * they did not authenticate before the LoginGraceTime alarm fired.
 */
struct early_child {
        int pipefd;
        int early;              /* Indicates child closed listener */
        char *id;               /* human readable connection identifier */
        pid_t pid;
        struct xaddr addr;
        int have_addr;
        int status, have_status;
        struct sshbuf *config;
        struct sshbuf *keys;
};
static struct early_child *children;
static int children_active;

/* sshd_config buffer */
struct sshbuf *cfg;
struct sshbuf *config;  /* packed */

/* Included files from the configuration file */
struct include_list includes = TAILQ_HEAD_INITIALIZER(includes);

/* message to be displayed after login */
struct sshbuf *loginmsg;

/* Unprivileged user */
struct passwd *privsep_pw = NULL;

static char *listener_proctitle;

/*
 * Close all listening sockets
 */
static void
close_listen_socks(void)
{
        int i;

        for (i = 0; i < num_listen_socks; i++)
                close(listen_socks[i]);
        num_listen_socks = 0;
}

/* Allocate and initialise the children array */
static void
child_alloc(void)
{
        int i;

        children = xcalloc(options.max_startups, sizeof(*children));
        for (i = 0; i < options.max_startups; i++) {
                children[i].pipefd = -1;
                children[i].pid = -1;
        }
}

/* Register a new connection in the children array; child pid comes later */
static struct early_child *
child_register(int pipefd, int sockfd)
{
        int i, lport, rport;
        char *laddr = NULL, *raddr = NULL;
        struct early_child *child = NULL;
        struct sockaddr_storage addr;
        socklen_t addrlen = sizeof(addr);
        struct sockaddr *sa = (struct sockaddr *)&addr;

        for (i = 0; i < options.max_startups; i++) {
                if (children[i].pipefd != -1 ||
                    children[i].config != NULL ||
                    children[i].keys != NULL ||
                    children[i].pid > 0)
                        continue;
                child = &(children[i]);
                break;
        }
        if (child == NULL) {
                fatal_f("error: accepted connection when all %d child "
                    " slots full", options.max_startups);
        }
        child->pipefd = pipefd;
        child->early = 1;
        if ((child->config = sshbuf_fromb(config)) == NULL)
                fatal_f("sshbuf_fromb failed");
        /* record peer address, if available */
        if (getpeername(sockfd, sa, &addrlen) == 0 &&
           addr_sa_to_xaddr(sa, addrlen, &child->addr) == 0)
                child->have_addr = 1;
        /* format peer address string for logs */
        if ((lport = get_local_port(sockfd)) == 0 ||
            (rport = get_peer_port(sockfd)) == 0) {
                /* Not a TCP socket */
                raddr = get_peer_ipaddr(sockfd);
                xasprintf(&child->id, "connection from %s", raddr);
        } else {
                laddr = get_local_ipaddr(sockfd);
                raddr = get_peer_ipaddr(sockfd);
                xasprintf(&child->id, "connection from %s to %s", raddr, laddr);
        }
        free(laddr);
        free(raddr);
        if (++children_active > options.max_startups)
                fatal_f("internal error: more children than max_startups");

        return child;
}

/*
 * Finally free a child entry. Don't call this directly.
 */
static void
child_finish(struct early_child *child)
{
        if (children_active == 0)
                fatal_f("internal error: children_active underflow");
        if (child->pipefd != -1)
                close(child->pipefd);
        sshbuf_free(child->config);
        sshbuf_free(child->keys);
        free(child->id);
        memset(child, '\0', sizeof(*child));
        child->pipefd = -1;
        child->pid = -1;
        children_active--;
}

/*
 * Close a child's pipe. This will not stop tracking the child immediately
 * (it will still be tracked for waitpid()) unless force_final is set, or
 * child has already exited.
 */
static void
child_close(struct early_child *child, int force_final, int quiet)
{
        if (!quiet)
                debug_f("enter%s", force_final ? " (forcing)" : "");
        if (child->pipefd != -1) {
                close(child->pipefd);
                child->pipefd = -1;
        }
        if (child->pid == -1 || force_final)
                child_finish(child);
}

/* Record a child exit. Safe to call from signal handlers */
static void
child_exit(pid_t pid, int status)
{
        int i;

        if (children == NULL || pid <= 0)
                return;
        for (i = 0; i < options.max_startups; i++) {
                if (children[i].pid == pid) {
                        children[i].have_status = 1;
                        children[i].status = status;
                        break;
                }
        }
}

/*
 * Reap a child entry that has exited, as previously flagged
 * using child_exit().
 * Handles logging of exit condition and will finalise the child if its pipe
 * had already been closed.
 */
static void
child_reap(struct early_child *child)
{
        LogLevel level = SYSLOG_LEVEL_DEBUG1;
        int was_crash, penalty_type = SRCLIMIT_PENALTY_NONE;
        const char *child_status;

        if (child->config)
                child_status = " (sending config)";
        else if (child->keys)
                child_status = " (sending keys)";
        else if (child->early)
                child_status = " (early)";
        else
                child_status = "";

        /* Log exit information */
        if (WIFSIGNALED(child->status)) {
                /*
                 * Increase logging for signals potentially associated
                 * with serious conditions.
                 */
                if ((was_crash = signal_is_crash(WTERMSIG(child->status))))
                        level = SYSLOG_LEVEL_ERROR;
                do_log2(level, "session process %ld for %s killed by "
                    "signal %d%s", (long)child->pid, child->id,
                    WTERMSIG(child->status), child_status);
                if (was_crash)
                        penalty_type = SRCLIMIT_PENALTY_CRASH;
        } else if (!WIFEXITED(child->status)) {
                penalty_type = SRCLIMIT_PENALTY_CRASH;
                error("session process %ld for %s terminated abnormally, "
                    "status=0x%x%s", (long)child->pid, child->id, child->status,
                    child_status);
        } else {
                /* Normal exit. We care about the status */
                switch (WEXITSTATUS(child->status)) {
                case 0:
                        debug3_f("preauth child %ld for %s completed "
                            "normally%s", (long)child->pid, child->id,
                            child_status);
                        break;
                case EXIT_LOGIN_GRACE:
                        penalty_type = SRCLIMIT_PENALTY_GRACE_EXCEEDED;
                        logit("Timeout before authentication for %s, "
                            "pid = %ld%s", child->id, (long)child->pid,
                            child_status);
                        break;
                case EXIT_CHILD_CRASH:
                        penalty_type = SRCLIMIT_PENALTY_CRASH;
                        logit("Session process %ld unpriv child crash for %s%s",
                            (long)child->pid, child->id, child_status);
                        break;
                case EXIT_AUTH_ATTEMPTED:
                        penalty_type = SRCLIMIT_PENALTY_AUTHFAIL;
                        debug_f("preauth child %ld for %s exited "
                            "after unsuccessful auth attempt%s",
                            (long)child->pid, child->id, child_status);
                        break;
                case EXIT_CONFIG_REFUSED:
                        penalty_type = SRCLIMIT_PENALTY_REFUSECONNECTION;
                        debug_f("preauth child %ld for %s prohibited by"
                            "RefuseConnection%s",
                            (long)child->pid, child->id, child_status);
                        break;
                default:
                        penalty_type = SRCLIMIT_PENALTY_NOAUTH;
                        debug_f("preauth child %ld for %s exited "
                            "with status %d%s", (long)child->pid, child->id,
                            WEXITSTATUS(child->status), child_status);
                        break;
                }
        }

        if (child->have_addr)
                srclimit_penalise(&child->addr, penalty_type);

        child->pid = -1;
        child->have_status = 0;
        if (child->pipefd == -1)
                child_finish(child);
}

/* Reap all children that have exited; called after SIGCHLD */
static void
child_reap_all_exited(void)
{
        int i;
        pid_t pid;
        int status;

        if (children == NULL)
                return;

        for (;;) {
                if ((pid = waitpid(-1, &status, WNOHANG)) == 0)
                        break;
                else if (pid == -1) {
                        if (errno == EINTR || errno == EAGAIN)
                                continue;
                        if (errno != ECHILD)
                                error_f("waitpid: %s", strerror(errno));
                        break;
                }
                child_exit(pid, status);
        }

        for (i = 0; i < options.max_startups; i++) {
                if (!children[i].have_status)
                        continue;
                child_reap(&(children[i]));
        }
}

static void
close_startup_pipes(void)
{
        int i;

        if (children == NULL)
                return;
        for (i = 0; i < options.max_startups; i++) {
                if (children[i].pipefd != -1)
                        child_close(&(children[i]), 1, 1);
        }
}

/* Called after SIGINFO */
static void
show_info(void)
{
        int i;
        const char *child_status;

        /* XXX print listening sockets here too */
        if (children == NULL)
                return;
        logit("%d active startups", children_active);
        for (i = 0; i < options.max_startups; i++) {
                if (children[i].pipefd == -1 && children[i].pid <= 0)
                        continue;
                if (children[i].config)
                        child_status = " (sending config)";
                else if (children[i].keys)
                        child_status = " (sending keys)";
                else if (children[i].early)
                        child_status = " (early)";
                else
                        child_status = "";
                logit("child %d: fd=%d pid=%ld %s%s", i, children[i].pipefd,
                    (long)children[i].pid, children[i].id, child_status);
        }
        srclimit_penalty_info();
}

/*
 * Signal handler for SIGHUP.  Sshd execs itself when it receives SIGHUP;
 * the effect is to reread the configuration file (and to regenerate
 * the server key).
 */

static void
sighup_handler(int sig)
{
        received_sighup = 1;
}

/*
 * Called from the main program after receiving SIGHUP.
 * Restarts the server.
 */
static void
sighup_restart(void)
{
        logit("Received SIGHUP; restarting.");
        if (options.pid_file != NULL)
                unlink(options.pid_file);
        platform_pre_restart();
        close_listen_socks();
        close_startup_pipes();
        ssh_signal(SIGHUP, SIG_IGN); /* will be restored after exec */
        execv(saved_argv[0], saved_argv);
        logit("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0],
            strerror(errno));
        exit(1);
}

/*
 * Generic signal handler for terminating signals in the master daemon.
 */
static void
sigterm_handler(int sig)
{
        received_sigterm = sig;
}

#ifdef SIGINFO
static void
siginfo_handler(int sig)
{
        received_siginfo = 1;
}
#endif

static void
main_sigchld_handler(int sig)
{
        received_sigchld = 1;
}

/*
 * returns 1 if connection should be dropped, 0 otherwise.
 * dropping starts at connection #max_startups_begin with a probability
 * of (max_startups_rate/100). the probability increases linearly until
 * all connections are dropped for startups > max_startups
 */
static int
should_drop_connection(int startups)
{
        int p, r;

        if (startups < options.max_startups_begin)
                return 0;
        if (startups >= options.max_startups)
                return 1;
        if (options.max_startups_rate == 100)
                return 1;

        p  = 100 - options.max_startups_rate;
        p *= startups - options.max_startups_begin;
        p /= options.max_startups - options.max_startups_begin;
        p += options.max_startups_rate;
        r = arc4random_uniform(100);

        debug_f("p %d, r %d", p, r);
        return (r < p) ? 1 : 0;
}

/*
 * Check whether connection should be accepted by MaxStartups or for penalty.
 * Returns 0 if the connection is accepted. If the connection is refused,
 * returns 1 and attempts to send notification to client.
 * Logs when the MaxStartups condition is entered or exited, and periodically
 * while in that state.
 */
static int
drop_connection(int sock, int startups, int notify_pipe)
{
        static struct log_ratelimit_ctx ratelimit_maxstartups;
        static struct log_ratelimit_ctx ratelimit_penalty;
        static int init_done;
        char *laddr, *raddr;
        const char *reason = NULL, *subreason = NULL;
        const char msg[] = "Not allowed at this time\r\n";
        struct log_ratelimit_ctx *rl = NULL;
        int ratelimited;
        u_int ndropped;

        if (!init_done) {
                init_done = 1;
                log_ratelimit_init(&ratelimit_maxstartups, 4, 60, 20, 5*60);
                log_ratelimit_init(&ratelimit_penalty, 8, 60, 30, 2*60);
        }

        /* PerSourcePenalties */
        if (!srclimit_penalty_check_allow(sock, &subreason)) {
                reason = "PerSourcePenalties";
                rl = &ratelimit_penalty;
        } else {
                /* MaxStartups */
                if (!should_drop_connection(startups) &&
                    srclimit_check_allow(sock, notify_pipe) == 1)
                        return 0;
                reason = "Maxstartups";
                rl = &ratelimit_maxstartups;
        }

        laddr = get_local_ipaddr(sock);
        raddr = get_peer_ipaddr(sock);
        ratelimited = log_ratelimit(rl, time(NULL), NULL, &ndropped);
        do_log2(ratelimited ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO,
            "drop connection #%d from [%s]:%d on [%s]:%d %s",
            startups,
            raddr, get_peer_port(sock),
            laddr, get_local_port(sock),
            subreason != NULL ? subreason : reason);
        free(laddr);
        free(raddr);
        if (ndropped != 0) {
                logit("%s logging rate-limited: additional %u connections "
                    "dropped", reason, ndropped);
        }

        /* best-effort notification to client */
        (void)write(sock, msg, sizeof(msg) - 1);
        return 1;
}

static void
usage(void)
{
        if (options.version_addendum != NULL &&
            *options.version_addendum != '\0')
                fprintf(stderr, "%s %s, %s\n",
                    SSH_RELEASE,
                    options.version_addendum, SSH_OPENSSL_VERSION);
        else
                fprintf(stderr, "%s, %s\n",
                    SSH_RELEASE, SSH_OPENSSL_VERSION);
        fprintf(stderr,
"usage: sshd [-46DdeGiqTtV] [-C connection_spec] [-c host_cert_file]\n"
"            [-E log_file] [-f config_file] [-g login_grace_time]\n"
"            [-h host_key_file] [-o option] [-p port] [-u len]\n"
        );
        exit(1);
}

static struct sshbuf *
pack_hostkeys(void)
{
        struct sshbuf *m = NULL, *keybuf = NULL, *hostkeys = NULL;
        int r;
        u_int i;
        size_t len;

        if ((m = sshbuf_new()) == NULL ||
            (keybuf = sshbuf_new()) == NULL ||
            (hostkeys = sshbuf_new()) == NULL)
                fatal_f("sshbuf_new failed");

        /* pack hostkeys into a string. Empty key slots get empty strings */
        for (i = 0; i < options.num_host_key_files; i++) {
                /* private key */
                sshbuf_reset(keybuf);
                if (sensitive_data.host_keys[i] != NULL &&
                    (r = sshkey_private_serialize(sensitive_data.host_keys[i],
                    keybuf)) != 0)
                        fatal_fr(r, "serialize hostkey private");
                if ((r = sshbuf_put_stringb(hostkeys, keybuf)) != 0)
                        fatal_fr(r, "compose hostkey private");
                /* public key */
                if (sensitive_data.host_pubkeys[i] != NULL) {
                        if ((r = sshkey_puts(sensitive_data.host_pubkeys[i],
                            hostkeys)) != 0)
                                fatal_fr(r, "compose hostkey public");
                } else {
                        if ((r = sshbuf_put_string(hostkeys, NULL, 0)) != 0)
                                fatal_fr(r, "compose hostkey empty public");
                }
                /* cert */
                if (sensitive_data.host_certificates[i] != NULL) {
                        if ((r = sshkey_puts(
                            sensitive_data.host_certificates[i],
                            hostkeys)) != 0)
                                fatal_fr(r, "compose host cert");
                } else {
                        if ((r = sshbuf_put_string(hostkeys, NULL, 0)) != 0)
                                fatal_fr(r, "compose host cert empty");
                }
        }

        if ((r = sshbuf_put_u32(m, 0)) != 0 ||
            (r = sshbuf_put_u8(m, 0)) != 0 ||
            (r = sshbuf_put_stringb(m, hostkeys)) != 0)
                fatal_fr(r, "compose message");
        if ((len = sshbuf_len(m)) < 5 || len > 0xffffffff)
                fatal_f("bad length %zu", len);
        POKE_U32(sshbuf_mutable_ptr(m), len - 4);

        sshbuf_free(keybuf);
        sshbuf_free(hostkeys);
        return m;
}

static struct sshbuf *
pack_config(struct sshbuf *conf)
{
        struct sshbuf *m = NULL, *inc = NULL;
        struct include_item *item = NULL;
        size_t len;
        int r;

        debug3_f("d config len %zu", sshbuf_len(conf));

        if ((m = sshbuf_new()) == NULL ||
            (inc = sshbuf_new()) == NULL)
                fatal_f("sshbuf_new failed");

        /* pack includes into a string */
        TAILQ_FOREACH(item, &includes, entry) {
                if ((r = sshbuf_put_cstring(inc, item->selector)) != 0 ||
                    (r = sshbuf_put_cstring(inc, item->filename)) != 0 ||
                    (r = sshbuf_put_stringb(inc, item->contents)) != 0)
                        fatal_fr(r, "compose includes");
        }

        if ((r = sshbuf_put_u32(m, 0)) != 0 ||
            (r = sshbuf_put_u8(m, 0)) != 0 ||
            (r = sshbuf_put_stringb(m, conf)) != 0 ||
            (r = sshbuf_put_u64(m, options.timing_secret)) != 0 ||
            (r = sshbuf_put_stringb(m, inc)) != 0)
                fatal_fr(r, "compose config");

        if ((len = sshbuf_len(m)) < 5 || len > 0xffffffff)
                fatal_f("bad length %zu", len);
        POKE_U32(sshbuf_mutable_ptr(m), len - 4);

        sshbuf_free(inc);

        debug3_f("done");
        return m;
}

/*
 * Protocol from reexec master to child:
 *      uint32  size
 *      uint8   type (ignored)
 *      string  configuration
 *      uint64  timing_secret
 *      string  included_files[] {
 *              string  selector
 *              string  filename
 *              string  contents
 *      }
 * Second message
 *      uint32  size
 *      uint8   type (ignored)
 *      string  host_keys[] {
 *              string private_key
 *              string public_key
 *              string certificate
 *      }
 */
/*
 * This function is used only if inet_flag or debug_flag is set,
 * otherwise the data is sent from the main poll loop.
 * It sends the config from a child process back to the parent.
 * The parent will read the config after exec.
 */
static void
send_rexec_state(int fd)
{
        struct sshbuf *keys;
        u_int mlen;
        pid_t pid;

        if ((pid = fork()) == -1)
                fatal_f("fork failed: %s", strerror(errno));
        if (pid != 0)
                return;

        debug3_f("entering fd = %d config len %zu", fd,
            sshbuf_len(config));

        mlen = sshbuf_len(config);
        if (atomicio(vwrite, fd, sshbuf_mutable_ptr(config), mlen) != mlen)
                error_f("write: %s", strerror(errno));

        keys = pack_hostkeys();
        mlen = sshbuf_len(keys);
        if (atomicio(vwrite, fd, sshbuf_mutable_ptr(keys), mlen) != mlen)
                error_f("write: %s", strerror(errno));

        sshbuf_free(keys);
        debug3_f("done");
        exit(0);
}

/*
 * Listen for TCP connections
 */
static void
listen_on_addrs(struct listenaddr *la)
{
        int ret, listen_sock;
        struct addrinfo *ai;
        char ntop[NI_MAXHOST], strport[NI_MAXSERV];

        for (ai = la->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 ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen,
                    ntop, sizeof(ntop), strport, sizeof(strport),
                    NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
                        error("getnameinfo failed: %.100s",
                            ssh_gai_strerror(ret));
                        continue;
                }
                /* Create socket for listening. */
                listen_sock = socket(ai->ai_family, ai->ai_socktype,
                    ai->ai_protocol);
                if (listen_sock == -1) {
                        /* kernel may not support ipv6 */
                        verbose("socket: %.100s", strerror(errno));
                        continue;
                }
                if (set_nonblock(listen_sock) == -1) {
                        close(listen_sock);
                        continue;
                }
                if (fcntl(listen_sock, F_SETFD, FD_CLOEXEC) == -1) {
                        verbose("socket: CLOEXEC: %s", strerror(errno));
                        close(listen_sock);
                        continue;
                }
                /* Socket options */
                set_reuseaddr(listen_sock);
                if (la->rdomain != NULL &&
                    set_rdomain(listen_sock, la->rdomain) == -1) {
                        close(listen_sock);
                        continue;
                }

                /* Only communicate in IPv6 over AF_INET6 sockets. */
                if (ai->ai_family == AF_INET6)
                        sock_set_v6only(listen_sock);

                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) == -1) {
                        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. */
                if (listen(listen_sock, SSH_LISTEN_BACKLOG) == -1)
                        fatal("listen on [%s]:%s: %.100s",
                            ntop, strport, strerror(errno));
                logit("Server listening on %s port %s%s%s.",
                    ntop, strport,
                    la->rdomain == NULL ? "" : " rdomain ",
                    la->rdomain == NULL ? "" : la->rdomain);
        }
}

static void
server_listen(void)
{
        u_int i;

        /* Initialise per-source limit tracking. */
        srclimit_init(options.max_startups,
            options.per_source_max_startups,
            options.per_source_masklen_ipv4,
            options.per_source_masklen_ipv6,
            &options.per_source_penalty,
            options.per_source_penalty_exempt);

        for (i = 0; i < options.num_listen_addrs; i++) {
                listen_on_addrs(&options.listen_addrs[i]);
                freeaddrinfo(options.listen_addrs[i].addrs);
                free(options.listen_addrs[i].rdomain);
                memset(&options.listen_addrs[i], 0,
                    sizeof(options.listen_addrs[i]));
        }
        free(options.listen_addrs);
        options.listen_addrs = NULL;
        options.num_listen_addrs = 0;

        if (!num_listen_socks)
                fatal("Cannot bind any address.");
}

/*
 * The main TCP accept loop. Note that, for the non-debug case, returns
 * from this function are in a forked subprocess.
 */
static void
server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s,
    int log_stderr)
{
        struct pollfd *pfd = NULL;
        int i, ret, npfd, r;
        int oactive = -1, listening = 0, lameduck = 0;
        int *startup_pollfd;
        ssize_t len;
        const u_char *ptr;
        char c = 0;
        struct sockaddr_storage from;
        struct early_child *child;
        struct sshbuf *buf;
        socklen_t fromlen;
        u_char rnd[256];
        sigset_t nsigset, osigset;
#ifdef LIBWRAP
        struct request_info req;

        request_init(&req, RQ_DAEMON, __progname, 0);
#endif

        /* pipes connected to unauthenticated child sshd processes */
        child_alloc();
        startup_pollfd = xcalloc(options.max_startups, sizeof(int));

        /*
         * Prepare signal mask that we use to block signals that might set
         * received_sigterm/hup/chld/info, so that we are guaranteed
         * to immediately wake up the ppoll if a signal is received after
         * the flag is checked.
         */
        sigemptyset(&nsigset);
        sigaddset(&nsigset, SIGHUP);
        sigaddset(&nsigset, SIGCHLD);
#ifdef SIGINFO
        sigaddset(&nsigset, SIGINFO);
#endif
        sigaddset(&nsigset, SIGTERM);
        sigaddset(&nsigset, SIGQUIT);

        /* sized for worst-case */
        pfd = xcalloc(num_listen_socks + options.max_startups,
            sizeof(struct pollfd));

        /*
         * Stay listening for connections until the system crashes or
         * the daemon is killed with a signal.
         */
        for (;;) {
                sigprocmask(SIG_BLOCK, &nsigset, &osigset);
                if (received_sigterm) {
                        logit("Received signal %d; terminating.",
                            (int) received_sigterm);
                        close_listen_socks();
                        if (options.pid_file != NULL)
                                unlink(options.pid_file);
                        exit(received_sigterm == SIGTERM ? 0 : 255);
                }
                if (received_sigchld) {
                        child_reap_all_exited();
                        received_sigchld = 0;
                }
                if (received_siginfo) {
                        show_info();
                        received_siginfo = 0;
                }
                if (oactive != children_active) {
                        setproctitle("%s [listener] %d of %d-%d startups",
                            listener_proctitle, children_active,
                            options.max_startups_begin, options.max_startups);
                        oactive = children_active;
                }
                if (received_sighup) {
                        if (!lameduck) {
                                debug("Received SIGHUP; waiting for children");
                                close_listen_socks();
                                lameduck = 1;
                        }
                        if (listening <= 0) {
                                sigprocmask(SIG_SETMASK, &osigset, NULL);
                                sighup_restart();
                        }
                }

                for (i = 0; i < num_listen_socks; i++) {
                        pfd[i].fd = listen_socks[i];
                        pfd[i].events = POLLIN;
                }
                npfd = num_listen_socks;
                for (i = 0; i < options.max_startups; i++) {
                        startup_pollfd[i] = -1;
                        if (children[i].pipefd != -1) {
                                pfd[npfd].fd = children[i].pipefd;
                                pfd[npfd].events = POLLIN;
                                if (children[i].config != NULL ||
                                    children[i].keys != NULL)
                                        pfd[npfd].events |= POLLOUT;
                                startup_pollfd[i] = npfd++;
                        }
                }

                /* Wait until a connection arrives or a child exits. */
                ret = ppoll(pfd, npfd, NULL, &osigset);
                if (ret == -1 && errno != EINTR) {
                        error("ppoll: %.100s", strerror(errno));
                        if (errno == EINVAL)
                                cleanup_exit(1); /* can't recover */
                }
                sigprocmask(SIG_SETMASK, &osigset, NULL);
                if (ret == -1)
                        continue;

                for (i = 0; i < options.max_startups; i++) {
                        if (children[i].pipefd == -1 ||
                            startup_pollfd[i] == -1 ||
                            !(pfd[startup_pollfd[i]].revents & POLLOUT))
                                continue;
                        if (children[i].config)
                                buf = children[i].config;
                        else if (children[i].keys)
                                buf = children[i].keys;
                        else {
                                error_f("no buffer to send");
                                continue;
                        }
                        ptr = sshbuf_ptr(buf);
                        len = sshbuf_len(buf);
                        ret = write(children[i].pipefd, ptr, len);
                        if (ret == -1 && (errno == EINTR || errno == EAGAIN))
                                continue;
                        if (ret <= 0) {
                                if (children[i].early)
                                        listening--;
                                srclimit_done(children[i].pipefd);
                                child_close(&(children[i]), 0, 0);
                                continue;
                        }
                        if (ret == len) {
                                /* finished sending buffer */
                                sshbuf_free(buf);
                                if (children[i].config == buf) {
                                        /* sent config, now send keys */
                                        children[i].config = NULL;
                                        children[i].keys = pack_hostkeys();
                                } else if (children[i].keys == buf) {
                                        /* sent both config and keys */
                                        children[i].keys = NULL;
                                } else {
                                        fatal("config buf not set");
                                }

                        } else {
                                if ((r = sshbuf_consume(buf, ret)) != 0)
                                        fatal_fr(r, "config buf inconsistent");
                        }
                }
                for (i = 0; i < options.max_startups; i++) {
                        if (children[i].pipefd == -1 ||
                            startup_pollfd[i] == -1 ||
                            !(pfd[startup_pollfd[i]].revents & (POLLIN|POLLHUP)))
                                continue;
                        switch (read(children[i].pipefd, &c, sizeof(c))) {
                        case -1:
                                if (errno == EINTR || errno == EAGAIN)
                                        continue;
                                if (errno != EPIPE) {
                                        error_f("startup pipe %d (fd=%d): "
                                            "read %s", i, children[i].pipefd,
                                            strerror(errno));
                                }
                                /* FALLTHROUGH */
                        case 0:
                                /* child exited preauth */
                                if (children[i].early)
                                        listening--;
                                srclimit_done(children[i].pipefd);
                                child_close(&(children[i]), 0, 0);
                                break;
                        case 1:
                                if (children[i].config) {
                                        error_f("startup pipe %d (fd=%d)"
                                            " early read", i, children[i].pipefd);
                                        if (children[i].early)
                                                listening--;
                                        if (children[i].pid > 0)
                                                kill(children[i].pid, SIGTERM);
                                        srclimit_done(children[i].pipefd);
                                        child_close(&(children[i]), 0, 0);
                                        break;
                                }
                                if (children[i].early && c == '\0') {
                                        /* child has finished preliminaries */
                                        listening--;
                                        children[i].early = 0;
                                        debug2_f("child %lu for %s received "
                                            "config", (long)children[i].pid,
                                            children[i].id);
                                } else if (!children[i].early && c == '\001') {
                                        /* child has completed auth */
                                        debug2_f("child %lu for %s auth done",
                                            (long)children[i].pid,
                                            children[i].id);
                                        child_close(&(children[i]), 1, 0);
                                } else {
                                        error_f("unexpected message 0x%02x "
                                            "child %ld for %s in state %d",
                                            (int)c, (long)children[i].pid,
                                            children[i].id, children[i].early);
                                }
                                break;
                        }
                }
                for (i = 0; i < num_listen_socks; i++) {
                        if (!(pfd[i].revents & POLLIN))
                                continue;
                        fromlen = sizeof(from);
                        *newsock = accept(listen_socks[i],
                            (struct sockaddr *)&from, &fromlen);
                        if (*newsock == -1) {
                                if (errno != EINTR && errno != EWOULDBLOCK &&
                                    errno != ECONNABORTED && errno != EAGAIN)
                                        error("accept: %.100s",
                                            strerror(errno));
                                if (errno == EMFILE || errno == ENFILE)
                                        usleep(100 * 1000);
                                continue;
                        }
#ifdef LIBWRAP
                        /* Check whether logins are denied from this host. */
                        request_set(&req, RQ_FILE, *newsock,
                            RQ_CLIENT_NAME, "", RQ_CLIENT_ADDR, "", 0);
                        sock_host(&req);
                        if (!hosts_access(&req)) {
                                const struct linger l = { .l_onoff = 1,
                                    .l_linger  = 0 };

                                (void )setsockopt(*newsock, SOL_SOCKET,
                                    SO_LINGER, &l, sizeof(l));
                                (void )close(*newsock);
                                /*
                                 * Mimic message from libwrap's refuse() as
                                 * precisely as we can afford.  The authentic
                                 * message prints the IP address and the
                                 * hostname it resolves to in parentheses.  If
                                 * the IP address cannot be resolved to a
                                 * hostname, the IP address will be repeated
                                 * in parentheses.  As name resolution in the
                                 * main server loop could stall, and logging
                                 * resolved names adds little or no value to
                                 * incident investigation, this implementation
                                 * only repeats the IP address in parentheses.
                                 * This should resemble librwap's refuse()
                                 * closely enough not to break auditing
                                 * software like sshguard or custom scripts.
                                 */
                                syslog(LOG_WARNING,
                                    "refused connect from %s (%s)",
                                    eval_hostaddr(req.client),
                                    eval_hostaddr(req.client));
                                debug("Connection refused by tcp wrapper");
                                continue;
                        }
#endif /* LIBWRAP */
                        if (unset_nonblock(*newsock) == -1) {
                                close(*newsock);
                                continue;
                        }
                        if (socketpair(AF_UNIX,
                            SOCK_STREAM, 0, config_s) == -1) {
                                error("reexec socketpair: %s",
                                    strerror(errno));
                                close(*newsock);
                                continue;
                        }
                        if (drop_connection(*newsock,
                            children_active, config_s[0])) {
                                close(*newsock);
                                close(config_s[0]);
                                close(config_s[1]);
                                continue;
                        }

                        /*
                         * Got connection.  Fork a child to handle it, unless
                         * we are in debugging mode.
                         */
                        if (debug_flag) {
                                /*
                                 * In debugging mode.  Close the listening
                                 * socket, and start processing the
                                 * connection without forking.
                                 */
                                debug("Server will not fork when running in debugging mode.");
                                close_listen_socks();
                                *sock_in = *newsock;
                                *sock_out = *newsock;
                                send_rexec_state(config_s[0]);
                                close(config_s[0]);
                                free(pfd);
                                return;
                        }

                        /*
                         * Normal production daemon.  Fork, and have
                         * the child process the connection. The
                         * parent continues listening.
                         */
                        platform_pre_fork();
                        set_nonblock(config_s[0]);
                        listening++;
                        child = child_register(config_s[0], *newsock);
                        if ((child->pid = fork()) == 0) {
                                /*
                                 * Child.  Close the listening and
                                 * max_startup sockets.  Start using
                                 * the accepted socket. Reinitialize
                                 * logging (since our pid has changed).
                                 * We return from this function to handle
                                 * the connection.
                                 */
                                platform_post_fork_child();
                                close_startup_pipes();
                                close_listen_socks();
                                *sock_in = *newsock;
                                *sock_out = *newsock;
                                log_init(__progname,
                                    options.log_level,
                                    options.log_facility,
                                    log_stderr);
                                close(config_s[0]);
                                free(pfd);
                                return;
                        }

                        /* Parent.  Stay in the loop. */
                        platform_post_fork_parent(child->pid);
                        if (child->pid == -1)
                                error("fork: %.100s", strerror(errno));
                        else
                                debug("Forked child %ld.", (long)child->pid);

                        close(config_s[1]);
                        close(*newsock);

                        /*
                         * Ensure that our random state differs
                         * from that of the child
                         */
                        arc4random_stir();
                        arc4random_buf(rnd, sizeof(rnd));
#ifdef WITH_OPENSSL
                        RAND_seed(rnd, sizeof(rnd));
                        if ((RAND_bytes((u_char *)rnd, 1)) != 1)
                                fatal("%s: RAND_bytes failed", __func__);
#endif
                        explicit_bzero(rnd, sizeof(rnd));
                }
        }
}

static void
accumulate_host_timing_secret(struct sshbuf *server_cfg,
    struct sshkey *key)
{
        static struct ssh_digest_ctx *ctx;
        u_char *hash;
        size_t len;
        struct sshbuf *buf;
        int r;

        if (ctx == NULL && (ctx = ssh_digest_start(SSH_DIGEST_SHA512)) == NULL)
                fatal_f("ssh_digest_start");
        if (key == NULL) { /* finalize */
                /* add server config in case we are using agent for host keys */
                if (ssh_digest_update(ctx, sshbuf_ptr(server_cfg),
                    sshbuf_len(server_cfg)) != 0)
                        fatal_f("ssh_digest_update");
                len = ssh_digest_bytes(SSH_DIGEST_SHA512);
                hash = xmalloc(len);
                if (ssh_digest_final(ctx, hash, len) != 0)
                        fatal_f("ssh_digest_final");
                options.timing_secret = PEEK_U64(hash);
                freezero(hash, len);
                ssh_digest_free(ctx);
                ctx = NULL;
                return;
        }
        if ((buf = sshbuf_new()) == NULL)
                fatal_f("could not allocate buffer");
        if ((r = sshkey_private_serialize(key, buf)) != 0)
                fatal_fr(r, "encode %s key", sshkey_ssh_name(key));
        if (ssh_digest_update(ctx, sshbuf_ptr(buf), sshbuf_len(buf)) != 0)
                fatal_f("ssh_digest_update");
        sshbuf_reset(buf);
        sshbuf_free(buf);
}

static char *
prepare_proctitle(int ac, char **av)
{
        char *ret = NULL;
        int i;

        for (i = 0; i < ac; i++)
                xextendf(&ret, " ", "%s", av[i]);
        return ret;
}

static void
print_config(struct connection_info *connection_info)
{
        connection_info->test = 1;
        parse_server_match_config(&options, &includes, connection_info);
        dump_config(&options);
        exit(0);
}

/*
 * Main program for the daemon.
 */
int
main(int ac, char **av)
{
        extern char *optarg;
        extern int optind;
        int log_stderr = 0, inetd_flag = 0, test_flag = 0, no_daemon_flag = 0;
        char *config_file_name = _PATH_SERVER_CONFIG_FILE;
        int r, opt, do_dump_cfg = 0, keytype, already_daemon, have_agent = 0;
        int sock_in = -1, sock_out = -1, newsock = -1, rexec_argc = 0;
        int devnull, config_s[2] = { -1 , -1 }, have_connection_info = 0;
        int need_chroot = 1;
        char *args, *fp, *line, *logfile = NULL, **rexec_argv = NULL;
        struct stat sb;
        u_int i, j;
        mode_t new_umask;
        struct sshkey *key;
        struct sshkey *pubkey;
        struct connection_info connection_info;
        struct utsname utsname;
        sigset_t sigmask;

        memset(&connection_info, 0, sizeof(connection_info));
#ifdef HAVE_SECUREWARE
        (void)set_auth_parameters(ac, av);
#endif
        __progname = ssh_get_progname(av[0]);

        sigemptyset(&sigmask);
        sigprocmask(SIG_SETMASK, &sigmask, NULL);

        /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */
        saved_argc = ac;
        rexec_argc = ac;
        saved_argv = xcalloc(ac + 1, sizeof(*saved_argv));
        for (i = 0; (int)i < ac; i++)
                saved_argv[i] = xstrdup(av[i]);
        saved_argv[i] = NULL;

#ifndef HAVE_SETPROCTITLE
        /* Prepare for later setproctitle emulation */
        compat_init_setproctitle(ac, av);
        av = saved_argv;
#endif

        if (geteuid() == 0 && setgroups(0, NULL) == -1)
                debug("setgroups(): %.200s", strerror(errno));

        /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
        sanitise_stdfd();

        /* Initialize configuration options to their default values. */
        initialize_server_options(&options);

        /* Parse command-line arguments. */
        args = argv_assemble(ac, av); /* logged later */
        while ((opt = getopt(ac, av,
            "C:E:b:c:f:g:h:k:o:p:u:46DGQRTdeiqrtV")) != -1) {
                switch (opt) {
                case '4':
                        options.address_family = AF_INET;
                        break;
                case '6':
                        options.address_family = AF_INET6;
                        break;
                case 'f':
                        config_file_name = optarg;
                        break;
                case 'c':
                        servconf_add_hostcert("[command-line]", 0,
                            &options, optarg);
                        break;
                case 'd':
                        if (debug_flag == 0) {
                                debug_flag = 1;
                                options.log_level = SYSLOG_LEVEL_DEBUG1;
                        } else if (options.log_level < SYSLOG_LEVEL_DEBUG3)
                                options.log_level++;
                        break;
                case 'D':
                        no_daemon_flag = 1;
                        break;
                case 'G':
                        do_dump_cfg = 1;
                        break;
                case 'E':
                        logfile = optarg;
                        /* FALLTHROUGH */
                case 'e':
                        log_stderr = 1;
                        break;
                case 'i':
                        inetd_flag = 1;
                        break;
                case 'r':
                        logit("-r option is deprecated");
                        break;
                case 'R':
                        fatal("-R not supported here");
                        break;
                case 'Q':
                        /* ignored */
                        break;
                case 'q':
                        options.log_level = SYSLOG_LEVEL_QUIET;
                        break;
                case 'b':
                        /* protocol 1, ignored */
                        break;
                case 'p':
                        options.ports_from_cmdline = 1;
                        if (options.num_ports >= MAX_PORTS) {
                                fprintf(stderr, "too many ports.\n");
                                exit(1);
                        }
                        options.ports[options.num_ports++] = a2port(optarg);
                        if (options.ports[options.num_ports-1] <= 0) {
                                fprintf(stderr, "Bad port number.\n");
                                exit(1);
                        }
                        break;
                case 'g':
                        if ((options.login_grace_time = convtime(optarg)) == -1) {
                                fprintf(stderr, "Invalid login grace time.\n");
                                exit(1);
                        }
                        break;
                case 'k':
                        /* protocol 1, ignored */
                        break;
                case 'h':
                        servconf_add_hostkey("[command-line]", 0,
                            &options, optarg, 1);
                        break;
                case 't':
                        test_flag = 1;
                        break;
                case 'T':
                        test_flag = 2;
                        break;
                case 'C':
                        if (parse_server_match_testspec(&connection_info,
                            optarg) == -1)
                                exit(1);
                        have_connection_info = 1;
                        break;
                case 'u':
                        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);
                        }
                        break;
                case 'o':
                        line = xstrdup(optarg);
                        if (process_server_config_line(&options, line,
                            "command-line", 0, NULL, NULL, &includes) != 0)
                                exit(1);
                        free(line);
                        break;
                case 'V':
                        fprintf(stderr, "%s, %s\n",
                            SSH_RELEASE, SSH_OPENSSL_VERSION);
                        exit(0);
                default:
                        usage();
                        break;
                }
        }
        if (!test_flag && !inetd_flag && !do_dump_cfg && !path_absolute(av[0]))
                fatal("sshd requires execution with an absolute path");

        closefrom(STDERR_FILENO + 1);

        /* Reserve fds we'll need later for reexec things */
        if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1)
                fatal("open %s: %s", _PATH_DEVNULL, strerror(errno));
        while (devnull < REEXEC_MIN_FREE_FD) {
                if ((devnull = dup(devnull)) == -1)
                        fatal("dup %s: %s", _PATH_DEVNULL, strerror(errno));
        }

        seed_rng();

        /* If requested, redirect the logs to the specified logfile. */
        if (logfile != NULL) {
                char *cp, pid_s[32];

                snprintf(pid_s, sizeof(pid_s), "%ld", (unsigned long)getpid());
                cp = percent_expand(logfile,
                    "p", pid_s,
                    "P", "sshd",
                    (char *)NULL);
                log_redirect_stderr_to(cp);
                free(cp);
        }

        /*
         * Force logging to stderr until we have loaded the private host
         * key (unless started from inetd)
         */
        log_init(__progname,
            options.log_level == SYSLOG_LEVEL_NOT_SET ?
            SYSLOG_LEVEL_INFO : options.log_level,
            options.log_facility == SYSLOG_FACILITY_NOT_SET ?
            SYSLOG_FACILITY_AUTH : options.log_facility,
            log_stderr || !inetd_flag || debug_flag);

        /*
         * Unset KRB5CCNAME, otherwise the user's session may inherit it from
         * root's environment
         */
        if (getenv("KRB5CCNAME") != NULL)
                (void) unsetenv("KRB5CCNAME");

        sensitive_data.have_ssh2_key = 0;

        /*
         * If we're not doing an extended test do not silently ignore connection
         * test params.
         */
        if (test_flag < 2 && have_connection_info)
                fatal("Config test connection parameter (-C) provided without "
                    "test mode (-T)");

        debug("sshd version %s, %s", SSH_VERSION, SSH_OPENSSL_VERSION);
        if (uname(&utsname) != 0) {
                memset(&utsname, 0, sizeof(utsname));
                strlcpy(utsname.sysname, "UNKNOWN", sizeof(utsname.sysname));
        }
        debug3("Running on %s %s %s %s", utsname.sysname, utsname.release,
            utsname.version, utsname.machine);
        debug3("Started with: %s", args);
        free(args);

        /* Fetch our configuration */
        if ((cfg = sshbuf_new()) == NULL)
                fatal("sshbuf_new config failed");
        if (strcasecmp(config_file_name, "none") != 0)
                load_server_config(config_file_name, cfg);

        parse_server_config(&options, config_file_name, cfg,
            &includes, NULL, 0);

        /* Fill in default values for those options not explicitly set. */
        fill_default_server_options(&options);

        /* 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");
        if (options.authorized_principals_command_user == NULL &&
            (options.authorized_principals_command != NULL &&
            strcasecmp(options.authorized_principals_command, "none") != 0))
                fatal("AuthorizedPrincipalsCommand set without "
                    "AuthorizedPrincipalsCommandUser");

        /*
         * 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 blocks, but this catches
         * and warns for trivial misconfigurations that could break login.
         */
        if (options.num_auth_methods != 0) {
                for (i = 0; i < options.num_auth_methods; i++) {
                        if (auth2_methods_valid(options.auth_methods[i],
                            1) == 0)
                                break;
                }
                if (i >= options.num_auth_methods)
                        fatal("AuthenticationMethods cannot be satisfied by "
                            "enabled authentication methods");
        }

        /* Check that there are no remaining arguments. */
        if (optind < ac) {
                fprintf(stderr, "Extra argument %s.\n", av[optind]);
                exit(1);
        }

        if (do_dump_cfg)
                print_config(&connection_info);

        /* load host keys */
        sensitive_data.host_keys = xcalloc(options.num_host_key_files,
            sizeof(struct sshkey *));
        sensitive_data.host_pubkeys = xcalloc(options.num_host_key_files,
            sizeof(struct sshkey *));

        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_r(r, "Could not connect to agent \"%s\"",
                            options.host_key_agent);
        }

        for (i = 0; i < options.num_host_key_files; i++) {
                int ll = options.host_key_file_userprovided[i] ?
                    SYSLOG_LEVEL_ERROR : SYSLOG_LEVEL_DEBUG1;

                if (options.host_key_files[i] == NULL)
                        continue;
                if ((r = sshkey_load_private(options.host_key_files[i], "",
                    &key, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR)
                        do_log2_r(r, ll, "Unable to load host key \"%s\"",
                            options.host_key_files[i]);
                if (sshkey_is_sk(key) &&
                    key->sk_flags & SSH_SK_USER_PRESENCE_REQD) {
                        debug("host key %s requires user presence, ignoring",
                            options.host_key_files[i]);
                        key->sk_flags &= ~SSH_SK_USER_PRESENCE_REQD;
                }
                if (r == 0 && key != NULL &&
                    (r = sshkey_shield_private(key)) != 0) {
                        do_log2_r(r, ll, "Unable to shield host key \"%s\"",
                            options.host_key_files[i]);
                        sshkey_free(key);
                        key = NULL;
                }
                if ((r = sshkey_load_public(options.host_key_files[i],
                    &pubkey, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR)
                        do_log2_r(r, ll, "Unable to load host key \"%s\"",
                            options.host_key_files[i]);
                if (pubkey != NULL && key != NULL) {
                        if (!sshkey_equal(pubkey, key)) {
                                error("Public key for %s does not match "
                                    "private key", options.host_key_files[i]);
                                sshkey_free(pubkey);
                                pubkey = NULL;
                        }
                }
                if (pubkey == NULL && key != NULL) {
                        if ((r = sshkey_from_private(key, &pubkey)) != 0)
                                fatal_r(r, "Could not demote key: \"%s\"",
                                    options.host_key_files[i]);
                }
                if (pubkey != NULL && (r = sshkey_check_rsa_length(pubkey,
                    options.required_rsa_size)) != 0) {
                        error_fr(r, "Host key %s", options.host_key_files[i]);
                        sshkey_free(pubkey);
                        sshkey_free(key);
                        continue;
                }
                sensitive_data.host_keys[i] = key;
                sensitive_data.host_pubkeys[i] = pubkey;

                if (key == NULL && pubkey != NULL && 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;
                        accumulate_host_timing_secret(cfg, key);
                } else {
                        do_log2(ll, "Unable to load host key: %s",
                            options.host_key_files[i]);
                        sensitive_data.host_keys[i] = NULL;
                        sensitive_data.host_pubkeys[i] = NULL;
                        continue;
                }

                switch (keytype) {
                case KEY_RSA:
                case KEY_DSA:
                case KEY_ECDSA:
                case KEY_ED25519:
                case KEY_ECDSA_SK:
                case KEY_ED25519_SK:
                case KEY_XMSS:
                        if (have_agent || key != NULL)
                                sensitive_data.have_ssh2_key = 1;
                        break;
                }
                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, sshkey_ssh_name(pubkey), fp);
                free(fp);
        }
        accumulate_host_timing_secret(cfg, NULL);
        if (!sensitive_data.have_ssh2_key) {
                logit("sshd: no hostkeys available -- exiting.");
                exit(1);
        }

        /*
         * Load certificates. They are stored in an array at identical
         * indices to the public keys that they relate to.
         */
        sensitive_data.host_certificates = xcalloc(options.num_host_key_files,
            sizeof(struct sshkey *));
        for (i = 0; i < options.num_host_key_files; i++)
                sensitive_data.host_certificates[i] = NULL;

        for (i = 0; i < options.num_host_cert_files; i++) {
                if (options.host_cert_files[i] == NULL)
                        continue;
                if ((r = sshkey_load_public(options.host_cert_files[i],
                    &key, NULL)) != 0) {
                        error_r(r, "Could not load host certificate \"%s\"",
                            options.host_cert_files[i]);
                        continue;
                }
                if (!sshkey_is_cert(key)) {
                        error("Certificate file is not a certificate: %s",
                            options.host_cert_files[i]);
                        sshkey_free(key);
                        continue;
                }
                /* Find matching private key */
                for (j = 0; j < options.num_host_key_files; j++) {
                        if (sshkey_equal_public(key,
                            sensitive_data.host_pubkeys[j])) {
                                sensitive_data.host_certificates[j] = key;
                                break;
                        }
                }
                if (j >= options.num_host_key_files) {
                        error("No matching private key for certificate: %s",
                            options.host_cert_files[i]);
                        sshkey_free(key);
                        continue;
                }
                sensitive_data.host_certificates[j] = key;
                debug("host certificate: #%u type %d %s", j, key->type,
                    sshkey_type(key));
        }

        /* Ensure privsep directory is correctly configured. */
        need_chroot = ((getuid() == 0 || geteuid() == 0) ||
            options.kerberos_authentication);
        if ((getpwnam(SSH_PRIVSEP_USER)) == NULL && need_chroot) {
                fatal("Privilege separation user %s does not exist",
                    SSH_PRIVSEP_USER);
        }
        endpwent();

        if (need_chroot) {
                if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &sb) == -1) ||
                    (S_ISDIR(sb.st_mode) == 0))
                        fatal("Missing privilege separation directory: %s",
                            _PATH_PRIVSEP_CHROOT_DIR);
#ifdef HAVE_CYGWIN
                if (check_ntsec(_PATH_PRIVSEP_CHROOT_DIR) &&
                    (sb.st_uid != getuid () ||
                    (sb.st_mode & (S_IWGRP|S_IWOTH)) != 0))
#else
                if (sb.st_uid != 0 || (sb.st_mode & (S_IWGRP|S_IWOTH)) != 0)
#endif
                        fatal("%s must be owned by root and not group or "
                            "world-writable.", _PATH_PRIVSEP_CHROOT_DIR);
        }

        if (test_flag > 1)
                print_config(&connection_info);

        /* Configuration looks good, so exit if in test mode. */
        if (test_flag)
                exit(0);

        /*
         * Clear out any supplemental groups we may have inherited.  This
         * prevents inadvertent creation of files with bad modes (in the
         * portable version at least, it's certainly possible for PAM
         * to create a file, and we can't control the code in every
         * module which might be used).
         */
        if (setgroups(0, NULL) < 0)
                debug("setgroups() failed: %.200s", strerror(errno));

        /* Prepare arguments for sshd-session */
        if (rexec_argc < 0)
                fatal("rexec_argc %d < 0", rexec_argc);
        rexec_argv = xcalloc(rexec_argc + 3, sizeof(char *));
        /* Point to the sshd-session binary instead of sshd */
        rexec_argv[0] = options.sshd_session_path;
        for (i = 1; i < (u_int)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] = NULL;
        if (stat(rexec_argv[0], &sb) != 0 || !(sb.st_mode & (S_IXOTH|S_IXUSR)))
                fatal("%s does not exist or is not executable", rexec_argv[0]);
        debug3("using %s for re-exec", rexec_argv[0]);

        /* Ensure that the privsep binary exists now too. */
        if (stat(options.sshd_auth_path, &sb) != 0 ||
            !(sb.st_mode & (S_IXOTH|S_IXUSR))) {
                fatal("%s does not exist or is not executable",
                    options.sshd_auth_path);
        }

        listener_proctitle = prepare_proctitle(ac, av);

        /* Ensure that umask disallows at least group and world write */
        new_umask = umask(0077) | 0022;
        (void) umask(new_umask);

        /* Initialize the log (it is reinitialized below in case we forked). */
        if (debug_flag && !inetd_flag)
                log_stderr = 1;
        log_init(__progname, options.log_level,
            options.log_facility, log_stderr);
        for (i = 0; i < options.num_log_verbose; i++)
                log_verbose_add(options.log_verbose[i]);

        /*
         * If not in debugging mode, not started from inetd and not already
         * daemonized (eg re-exec via SIGHUP), disconnect from the controlling
         * terminal, and fork.  The original process exits.
         */
        already_daemon = daemonized();
        if (!(debug_flag || inetd_flag || no_daemon_flag || already_daemon)) {

                if (daemon(0, 0) == -1)
                        fatal("daemon() failed: %.200s", strerror(errno));

                disconnect_controlling_tty();
        }
        /* Reinitialize the log (because of the fork above). */
        log_init(__progname, options.log_level, options.log_facility, log_stderr);

        /* Avoid killing the process in high-pressure swapping environments. */
        if (!inetd_flag && madvise(NULL, 0, MADV_PROTECT) != 0)
                debug("madvise(): %.200s", strerror(errno));

        /*
         * Chdir to the root directory so that the current disk can be
         * unmounted if desired.
         */
        if (chdir("/") == -1)
                error("chdir(\"/\"): %s", strerror(errno));

        /* ignore SIGPIPE */
        ssh_signal(SIGPIPE, SIG_IGN);

        config = pack_config(cfg);

        /* Get a connection, either from inetd or a listening TCP socket */
        if (inetd_flag) {
                /* Send configuration to ancestor sshd-session process */
                if (socketpair(AF_UNIX, SOCK_STREAM, 0, config_s) == -1)
                        fatal("socketpair: %s", strerror(errno));
                send_rexec_state(config_s[0]);
                close(config_s[0]);
        } else {
                platform_pre_listen();
                server_listen();

                ssh_signal(SIGHUP, sighup_handler);
                ssh_signal(SIGCHLD, main_sigchld_handler);
                ssh_signal(SIGTERM, sigterm_handler);
                ssh_signal(SIGQUIT, sigterm_handler);
#ifdef SIGINFO
                ssh_signal(SIGINFO, siginfo_handler);
#endif

                platform_post_listen();

                /*
                 * Write out the pid file after the sigterm handler
                 * is setup and the listen sockets are bound
                 */
                if (options.pid_file != NULL && !debug_flag) {
                        FILE *f = fopen(options.pid_file, "w");

                        if (f == NULL) {
                                error("Couldn't create pid file \"%s\": %s",
                                    options.pid_file, strerror(errno));
                        } else {
                                fprintf(f, "%ld\n", (long) getpid());
                                fclose(f);
                        }
                }

                /* Accept a connection and return in a forked child */
                server_accept_loop(&sock_in, &sock_out,
                    &newsock, config_s, log_stderr);
        }

        /* This is the child processing a new connection. */
        setproctitle("%s", "[accepted]");

        /*
         * Create a new session and process group since the 4.4BSD
         * setlogin() affects the entire process group.  We don't
         * want the child to be able to affect the parent.
         */
        if (!debug_flag && !inetd_flag && setsid() == -1)
                error("setsid: %.100s", strerror(errno));

        debug("rexec start in %d out %d newsock %d config_s %d/%d",
            sock_in, sock_out, newsock, config_s[0], config_s[1]);
        if (!inetd_flag) {
                if (dup2(newsock, STDIN_FILENO) == -1)
                        fatal("dup2 stdin: %s", strerror(errno));
                if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1)
                        fatal("dup2 stdout: %s", strerror(errno));
                if (newsock > STDOUT_FILENO)
                        close(newsock);
        }
        if (config_s[1] != REEXEC_CONFIG_PASS_FD) {
                if (dup2(config_s[1], REEXEC_CONFIG_PASS_FD) == -1)
                        fatal("dup2 config_s: %s", strerror(errno));
                close(config_s[1]);
        }
        log_redirect_stderr_to(NULL);
        closefrom(REEXEC_MIN_FREE_FD);

        ssh_signal(SIGHUP, SIG_IGN); /* avoid reset to SIG_DFL */
        execv(rexec_argv[0], rexec_argv);

        fatal("rexec of %s failed: %s", rexec_argv[0], strerror(errno));
#ifdef __FreeBSD__
        /*
         * Initialize the resolver.  This may not happen automatically
         * before privsep chroot().
         */
        if ((_res.options & RES_INIT) == 0) {
                debug("res_init()");
                res_init();
        }
#ifdef GSSAPI
        /*
         * Force GSS-API to parse its configuration and load any
         * mechanism plugins.
         */
        {
                gss_OID_set mechs;
                OM_uint32 minor_status;
                gss_indicate_mechs(&minor_status, &mechs);
                gss_release_oid_set(&minor_status, &mechs);
        }
#endif
#endif
}

/* server specific fatal cleanup */
void
cleanup_exit(int i)
{
        _exit(i);
}