]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: big refactor of sshd config management code.
authordjm@openbsd.org <djm@openbsd.org>
Sun, 31 May 2026 11:30:50 +0000 (11:30 +0000)
committerDamien Miller <djm@mindrot.org>
Sun, 31 May 2026 12:23:40 +0000 (22:23 +1000)
This generates much of the initialisation, defaults and keyword table
code from a set of macros rather than hand coding them. These same
macros are also used to generate serialisation and deserialisation
code.

The macros are admittedly ugly but have the advantage of forcing a
good degree of consistency across places that need to stay in sync
with each other.

The new de/serialisation code is used to pass configurations across
the various sshd-* process boundaries. This removes the need to pass
around raw text configurations that need to be re-parsed as well as
eliminating some raw pointer leakage across the processes where
structures were previously clumsily serialised.

feedback/ok markus@

OpenBSD-Commit-ID: 9a5109a480637e08c290eeb82aa8ef2ca7e848ce

monitor.c
monitor_wrap.c
monitor_wrap.h
servconf.c
servconf.h
sshd-auth.c

index 9d6672bb628e3379fda5d96e23133b9071f716e1..73a85408e400e5b9723ac42d432a2b06829f019e 100644 (file)
--- a/monitor.c
+++ b/monitor.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.255 2026/03/28 05:06:16 djm Exp $ */
+/* $OpenBSD: monitor.c,v 1.256 2026/05/31 11:30:50 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -592,34 +592,26 @@ monitor_reset_key_state(void)
 int
 mm_answer_state(struct ssh *ssh, int sock, struct sshbuf *unused)
 {
-       struct sshbuf *m = NULL, *inc = NULL, *hostkeys = NULL;
+       struct sshbuf *m = NULL, *config = NULL, *hostkeys = NULL;
        struct sshbuf *opts = NULL, *confdata = NULL;
-       struct include_item *item = NULL;
        int postauth;
        int r;
 
        debug_f("config len %zu", sshbuf_len(cfg));
 
        if ((m = sshbuf_new()) == NULL ||
-           (inc = sshbuf_new()) == NULL ||
+           (config = sshbuf_new()) == NULL ||
            (opts = sshbuf_new()) == NULL ||
            (confdata = sshbuf_new()) == NULL)
                fatal_f("sshbuf_new failed");
 
-       /* XXX unnecessary? */
-       /* 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");
-       }
-
        hostkeys = pack_hostkeys();
+       if ((r = serialise_server_options(&options, &config)) != 0)
+               fatal_fr(r, "serialise_server_options");
 
        /*
         * Protocol from monitor to unpriv privsep process:
-        *      string  configuration
+        *      string  configuration_blob
         *      uint64  timing_secret   XXX move delays to monitor and remove
         *      string  host_keys[] {
         *              string public_key
@@ -627,23 +619,17 @@ mm_answer_state(struct ssh *ssh, int sock, struct sshbuf *unused)
         *      }
         *      string  server_banner
         *      string  client_banner
-        *      string  included_files[] {
-        *              string  selector
-        *              string  filename
-        *              string  contents
-        *      }
         *      string  configuration_data (postauth)
         *      string  keystate (postauth)
         *      string  authenticated_user (postauth)
         *      string  session_info (postauth)
         *      string  authopts (postauth)
         */
-       if ((r = sshbuf_put_stringb(m, cfg)) != 0 ||
+       if ((r = sshbuf_put_stringb(m, config)) != 0 ||
            (r = sshbuf_put_u64(m, options.timing_secret)) != 0 ||
            (r = sshbuf_put_stringb(m, hostkeys)) != 0 ||
            (r = sshbuf_put_stringb(m, ssh->kex->server_version)) != 0 ||
-           (r = sshbuf_put_stringb(m, ssh->kex->client_version)) != 0 ||
-           (r = sshbuf_put_stringb(m, inc)) != 0)
+           (r = sshbuf_put_stringb(m, ssh->kex->client_version)) != 0)
                fatal_fr(r, "compose config");
 
        postauth = (authctxt && authctxt->pw && authctxt->authenticated);
@@ -652,7 +638,7 @@ mm_answer_state(struct ssh *ssh, int sock, struct sshbuf *unused)
                fatal_f("internal error: called in postauth");
        }
 
-       sshbuf_free(inc);
+       sshbuf_free(config);
        sshbuf_free(opts);
        sshbuf_free(confdata);
        sshbuf_free(hostkeys);
@@ -832,27 +818,15 @@ void
 mm_encode_server_options(struct sshbuf *m)
 {
        int r;
-       u_int i;
+       struct sshbuf *config;
 
-       /* XXX this leaks raw pointers to the unpriv child processes */
-       if ((r = sshbuf_put_string(m, &options, sizeof(options))) != 0)
+       if ((config = sshbuf_new()) == NULL)
+               fatal_f("sshbuf_new failed");
+       if ((r = serialise_server_options(&options, &config)) != 0)
+               fatal_fr(r, "serialise_server_options");
+       if ((r = sshbuf_put_stringb(m, config)) != 0)
                fatal_fr(r, "assemble options");
-
-#define M_CP_STROPT(x) do { \
-               if (options.x != NULL && \
-                   (r = sshbuf_put_cstring(m, options.x)) != 0) \
-                       fatal_fr(r, "assemble %s", #x); \
-       } while (0)
-#define M_CP_STRARRAYOPT(x, nx, clobber) do { \
-               for (i = 0; i < options.nx; i++) { \
-                       if ((r = sshbuf_put_cstring(m, options.x[i])) != 0) \
-                               fatal_fr(r, "assemble %s", #x); \
-               } \
-       } while (0)
-       /* See comment in servconf.h */
-       COPY_MATCH_STRING_OPTS();
-#undef M_CP_STROPT
-#undef M_CP_STRARRAYOPT
+       sshbuf_free(config);
 }
 
 /* Retrieves the password entry and also checks if the user is permitted */
index 81596a4cc66b157a6ad19c009c4762d91c375c85..e2b9a2802898880fc8a4f71ae3ed9553e080ff91 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor_wrap.c,v 1.146 2026/03/02 02:40:15 djm Exp $ */
+/* $OpenBSD: monitor_wrap.c,v 1.147 2026/05/31 11:30:50 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -302,55 +302,20 @@ mm_sshkey_sign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp,
 void
 mm_decode_activate_server_options(struct ssh *ssh, struct sshbuf *m)
 {
-       const u_char *p;
-       size_t len;
-       u_int i;
-       ServerOptions *newopts;
+       struct sshbuf *config;
        int r;
+       u_int i;
 
-       if ((r = sshbuf_get_string_direct(m, &p, &len)) != 0)
+       if ((r = sshbuf_froms(m, &config)) != 0)
                fatal_fr(r, "parse opts");
-       if (len != sizeof(*newopts))
-               fatal_f("option block size mismatch");
-       newopts = xcalloc(sizeof(*newopts), 1);
-       memcpy(newopts, p, sizeof(*newopts));
-
-#define M_CP_STROPT(x) do { \
-               if (newopts->x != NULL && \
-                   (r = sshbuf_get_cstring(m, &newopts->x, NULL)) != 0) \
-                       fatal_fr(r, "parse %s", #x); \
-       } while (0)
-#define M_CP_STRARRAYOPT(x, nx, clobber) do { \
-               newopts->x = newopts->nx == 0 ? \
-                   NULL : xcalloc(newopts->nx, sizeof(*newopts->x)); \
-               for (i = 0; i < newopts->nx; i++) { \
-                       if ((r = sshbuf_get_cstring(m, \
-                           &newopts->x[i], NULL)) != 0) \
-                               fatal_fr(r, "parse %s", #x); \
-               } \
-       } while (0)
-       /* See comment in servconf.h */
-       COPY_MATCH_STRING_OPTS();
-#undef M_CP_STROPT
-#undef M_CP_STRARRAYOPT
+       if ((r = deserialise_server_options(config, &options)) != 0)
+               fatal_fr(r, "deserialise_server_options");
+       sshbuf_free(config);
 
-       copy_set_server_options(&options, newopts, 1);
        log_change_level(options.log_level);
        log_verbose_reset();
        for (i = 0; i < options.num_log_verbose; i++)
                log_verbose_add(options.log_verbose[i]);
-
-       /* use the macro hell to clean up too */
-#define M_CP_STROPT(x) free(newopts->x)
-#define M_CP_STRARRAYOPT(x, nx, clobber) do { \
-               for (i = 0; i < newopts->nx; i++) \
-                       free(newopts->x[i]); \
-               free(newopts->x); \
-       } while (0)
-       COPY_MATCH_STRING_OPTS();
-#undef M_CP_STROPT
-#undef M_CP_STRARRAYOPT
-       free(newopts);
 }
 
 #define GETPW(b, id) \
@@ -885,22 +850,19 @@ mm_terminate(void)
 /* Request state information */
 
 void
-mm_get_state(struct ssh *ssh, struct include_list *includes,
-    struct sshbuf *conf, struct sshbuf **confdatap,
+mm_get_state(struct ssh *ssh,
+    ServerOptions *opts, struct sshbuf **confdatap,
     uint64_t *timing_secretp,
     struct sshbuf **hostkeysp, struct sshbuf **keystatep,
     u_char **pw_namep,
     struct sshbuf **authinfop, struct sshbuf **auth_optsp)
 {
-       struct sshbuf *m, *inc;
-       u_char *cp;
-       size_t len;
+       struct sshbuf *m, *config;
        int r;
-       struct include_item *item;
 
        debug3_f("entering");
 
-       if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL)
+       if ((m = sshbuf_new()) == NULL || (config = sshbuf_new()) == NULL)
                fatal_f("sshbuf_new failed");
 
        mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_STATE, m);
@@ -909,12 +871,11 @@ mm_get_state(struct ssh *ssh, struct include_list *includes,
        mm_request_receive_expect(pmonitor->m_recvfd,
            MONITOR_ANS_STATE, m);
 
-       if ((r = sshbuf_get_string(m, &cp, &len)) != 0 ||
+       if ((r = sshbuf_froms(m, &config)) != 0 ||
            (r = sshbuf_get_u64(m, timing_secretp)) != 0 ||
            (r = sshbuf_froms(m, hostkeysp)) != 0 ||
            (r = sshbuf_get_stringb(m, ssh->kex->server_version)) != 0 ||
-           (r = sshbuf_get_stringb(m, ssh->kex->client_version)) != 0 ||
-           (r = sshbuf_get_stringb(m, inc)) != 0)
+           (r = sshbuf_get_stringb(m, ssh->kex->client_version)) != 0)
                fatal_fr(r, "parse config");
 
        /* postauth */
@@ -926,24 +887,11 @@ mm_get_state(struct ssh *ssh, struct include_list *includes,
                    (r = sshbuf_froms(m, auth_optsp)) != 0)
                        fatal_fr(r, "parse config postauth");
        }
+       if ((r = deserialise_server_options(config, opts)) != 0)
+               fatal_fr(r, "deserialise_server_options");
 
-       if (conf != NULL && (r = sshbuf_put(conf, cp, len)))
-               fatal_fr(r, "sshbuf_put");
-
-       while (sshbuf_len(inc) != 0) {
-               item = xcalloc(1, sizeof(*item));
-               if ((item->contents = sshbuf_new()) == NULL)
-                       fatal_f("sshbuf_new failed");
-               if ((r = sshbuf_get_cstring(inc, &item->selector, NULL)) != 0 ||
-                   (r = sshbuf_get_cstring(inc, &item->filename, NULL)) != 0 ||
-                   (r = sshbuf_get_stringb(inc, item->contents)) != 0)
-                       fatal_fr(r, "parse includes");
-               TAILQ_INSERT_TAIL(includes, item, entry);
-       }
-
-       free(cp);
        sshbuf_free(m);
-       sshbuf_free(inc);
+       sshbuf_free(config);
 
        debug3_f("done");
 }
index c2f7f97d977acbfd503293fe4ca63f1538c87944..f43759b21d5c7c883d88b27c2d44f7436fc49736 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor_wrap.h,v 1.54 2026/03/02 02:40:15 djm Exp $ */
+/* $OpenBSD: monitor_wrap.h,v 1.55 2026/05/31 11:30:50 djm Exp $ */
 
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
@@ -96,8 +96,8 @@ void mm_session_pty_cleanup2(struct Session *);
 void mm_send_keystate(struct ssh *, struct monitor*);
 
 /* state */
-struct include_list;
-void mm_get_state(struct ssh *, struct include_list *, struct sshbuf *,
+struct ServerOptions;
+void mm_get_state(struct ssh *, struct ServerOptions *,
     struct sshbuf **, uint64_t *, struct sshbuf **, struct sshbuf **,
     u_char **, struct sshbuf **, struct sshbuf **);
 
index 668259a2074bd298bc256d20ef9e5f7bbf1b19a1..b378a8aee719bd06d4ae1c5553c0fcf90314f557 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.c,v 1.446 2026/04/02 07:38:14 djm Exp $ */
+/* $OpenBSD: servconf.c,v 1.447 2026/05/31 11:30:50 djm Exp $ */
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -63,6 +63,8 @@
 #include "digest.h"
 #include "version.h"
 
+#define SSHD_CONFIG_BLOB_VERSION       1
+
 #if !defined(SSHD_PAM_SERVICE)
 # define SSHD_PAM_SERVICE              "sshd"
 #endif
@@ -83,136 +85,107 @@ void
 initialize_server_options(ServerOptions *options)
 {
        memset(options, 0, sizeof(*options));
-
-       /* Portable-specific options */
-       options->use_pam = -1;
-       options->pam_service_name = NULL;
-
-       /* Standard Options */
-       options->num_ports = 0;
-       options->ports_from_cmdline = 0;
-       options->queued_listen_addrs = NULL;
-       options->num_queued_listens = 0;
-       options->listen_addrs = NULL;
+#define SSHCONF_INT(var, conf, flags, ms, def, cp)     options->var = -1;
+#define SSHCONF_INTFLAG(var, conf, flags, def, cp)     options->var = -1;
+#define SSHCONF_STRING(var, conf, flags, cp)           options->var = NULL;
+#define SSHCONF_STRARRAY(var, nvar, conf, flags, cp) \
+       options->nvar = 0; \
+       options->var = NULL;
+#define SSHCONF_CUSTOM(conf, funcsuffix, flags, cp) \
+       init_##funcsuffix(options)
+#define SSHCONF_NONCONF(funcsuffix) \
+       init_##funcsuffix(options)
+#define SSHCONF_NOSUPPORT(var, conf, opcode, flags)    /* empty */
+#define SSHCONF_ALIAS(old, conf, flags)                        /* empty */
+
+       /* Using macros for these is a bit overkill but forces consistency */
+#define init_hostkeyfile(options) \
+       options->host_key_files = 0; \
+       options->num_host_key_files = 0; \
+       options->host_key_file_userprovided = NULL;
+#define init_ipqos(options) \
+       options->ip_qos_interactive = -1; \
+       options->ip_qos_bulk = -1;
+#define init_listenaddress(options) \
+       options->queued_listen_addrs = NULL; \
+       options->num_queued_listens = 0; \
+       options->listen_addrs = NULL; \
        options->num_listen_addrs = 0;
-       options->address_family = -1;
-       options->routing_domain = NULL;
-       options->num_host_key_files = 0;
-       options->num_host_cert_files = 0;
-       options->host_key_agent = NULL;
-       options->pid_file = NULL;
-       options->login_grace_time = -1;
-       options->permit_root_login = PERMIT_NOT_SET;
-       options->ignore_rhosts = -1;
-       options->ignore_user_known_hosts = -1;
-       options->print_motd = -1;
-       options->print_lastlog = -1;
-       options->x11_forwarding = -1;
-       options->x11_display_offset = -1;
-       options->x11_use_localhost = -1;
-       options->permit_tty = -1;
-       options->permit_user_rc = -1;
-       options->xauth_location = NULL;
-       options->strict_modes = -1;
-       options->tcp_keep_alive = -1;
+#define init_logfacility(options) \
        options->log_facility = SYSLOG_FACILITY_NOT_SET;
+#define init_loglevel(options) \
        options->log_level = SYSLOG_LEVEL_NOT_SET;
-       options->num_log_verbose = 0;
-       options->log_verbose = NULL;
-       options->hostbased_authentication = -1;
-       options->hostbased_uses_name_from_packet_only = -1;
-       options->hostbased_accepted_algos = NULL;
-       options->hostkeyalgorithms = NULL;
-       options->pubkey_authentication = -1;
-       options->pubkey_auth_options = -1;
-       options->pubkey_accepted_algos = NULL;
-       options->kerberos_authentication = -1;
-       options->kerberos_or_local_passwd = -1;
-       options->kerberos_ticket_cleanup = -1;
-       options->kerberos_get_afs_token = -1;
-       options->gss_authentication=-1;
-       options->gss_cleanup_creds = -1;
-       options->gss_deleg_creds = -1;
-       options->gss_strict_acceptor = -1;
-       options->password_authentication = -1;
-       options->kbd_interactive_authentication = -1;
-       options->permit_empty_passwd = -1;
-       options->permit_user_env = -1;
-       options->permit_user_env_allowlist = NULL;
-       options->compression = -1;
-       options->rekey_limit = -1;
-       options->rekey_interval = -1;
-       options->allow_tcp_forwarding = -1;
-       options->allow_streamlocal_forwarding = -1;
-       options->allow_agent_forwarding = -1;
-       options->num_allow_users = 0;
-       options->num_deny_users = 0;
-       options->num_allow_groups = 0;
-       options->num_deny_groups = 0;
-       options->ciphers = NULL;
-       options->macs = NULL;
-       options->kex_algorithms = NULL;
-       options->ca_sign_algorithms = NULL;
+#define init_port(options) \
+       options->num_ports = 0; \
+       options->ports_from_cmdline = 0;
+#define init_gatewayports(options) \
        options->fwd_opts.gateway_ports = -1;
+#define init_streamlocalbindmask(options) \
        options->fwd_opts.streamlocal_bind_mask = (mode_t)-1;
+#define init_streamlocalbindunlink(options) \
        options->fwd_opts.streamlocal_bind_unlink = -1;
-       options->num_subsystems = 0;
-       options->max_startups_begin = -1;
-       options->max_startups_rate = -1;
+#define init_maxstartups(options) \
+       options->max_startups_begin = -1; \
+       options->max_startups_rate = -1; \
        options->max_startups = -1;
-       options->per_source_max_startups = -1;
-       options->per_source_masklen_ipv4 = -1;
+#define init_permituserenv(options) \
+       options->permit_user_env = -1; \
+       options->permit_user_env_allowlist = NULL;
+#define init_persourcenetblocksize(options) \
+       options->per_source_masklen_ipv4 = -1; \
        options->per_source_masklen_ipv6 = -1;
-       options->per_source_penalty_exempt = NULL;
-       options->per_source_penalty.enabled = -1;
-       options->per_source_penalty.max_sources4 = -1;
-       options->per_source_penalty.max_sources6 = -1;
-       options->per_source_penalty.overflow_mode = -1;
-       options->per_source_penalty.overflow_mode6 = -1;
-       options->per_source_penalty.penalty_crash = -1.0;
-       options->per_source_penalty.penalty_authfail = -1.0;
-       options->per_source_penalty.penalty_invaliduser = -1.0;
-       options->per_source_penalty.penalty_noauth = -1.0;
-       options->per_source_penalty.penalty_grace = -1.0;
-       options->per_source_penalty.penalty_refuseconnection = -1.0;
-       options->per_source_penalty.penalty_max = -1.0;
+#define init_persourcepenalties(options) \
+       options->per_source_penalty_exempt = NULL; \
+       options->per_source_penalty.enabled = -1; \
+       options->per_source_penalty.max_sources4 = -1; \
+       options->per_source_penalty.max_sources6 = -1; \
+       options->per_source_penalty.overflow_mode = -1; \
+       options->per_source_penalty.overflow_mode6 = -1; \
+       options->per_source_penalty.penalty_crash = -1.0; \
+       options->per_source_penalty.penalty_authfail = -1.0; \
+       options->per_source_penalty.penalty_invaliduser = -1.0; \
+       options->per_source_penalty.penalty_noauth = -1.0; \
+       options->per_source_penalty.penalty_grace = -1.0; \
+       options->per_source_penalty.penalty_refuseconnection = -1.0; \
+       options->per_source_penalty.penalty_max = -1.0; \
        options->per_source_penalty.penalty_min = -1.0;
-       options->max_authtries = -1;
-       options->max_sessions = -1;
-       options->banner = NULL;
-       options->use_dns = -1;
-       options->client_alive_interval = -1;
-       options->client_alive_count_max = -1;
-       options->num_authkeys_files = 0;
-       options->num_accept_env = 0;
-       options->num_setenv = 0;
-       options->permit_tun = -1;
-       options->permitted_opens = NULL;
-       options->permitted_listens = NULL;
-       options->adm_forced_command = NULL;
-       options->chroot_directory = NULL;
-       options->authorized_keys_command = NULL;
-       options->authorized_keys_command_user = NULL;
-       options->revoked_keys_files = NULL;
-       options->num_revoked_keys_files = 0;
-       options->sk_provider = NULL;
-       options->trusted_user_ca_keys = NULL;
-       options->authorized_principals_file = NULL;
-       options->authorized_principals_command = NULL;
-       options->authorized_principals_command_user = NULL;
-       options->ip_qos_interactive = -1;
-       options->ip_qos_bulk = -1;
-       options->version_addendum = NULL;
-       options->fingerprint_hash = -1;
-       options->disable_forwarding = -1;
-       options->expose_userauth_info = -1;
-       options->required_rsa_size = -1;
-       options->channel_timeouts = NULL;
-       options->num_channel_timeouts = 0;
-       options->unused_connection_timeout = -1;
-       options->sshd_session_path = NULL;
-       options->sshd_auth_path = NULL;
-       options->refuse_connection = -1;
+#define init_rekeylimit(options) \
+       options->rekey_limit = -1; \
+       options->rekey_interval = -1;
+#define init_subsystem(options) \
+       options->num_subsystems = 0; \
+       options->subsystem_name = NULL; \
+       options->subsystem_command = NULL; \
+       options->subsystem_args = NULL;
+#define init_timingsecret(options) \
+       options->timing_secret = 0;
+
+       SSHD_CONFIG_ENTRIES
+
+#undef init_hostkeyfile
+#undef init_ipqos
+#undef init_listenaddress
+#undef init_logfacility
+#undef init_loglevel
+#undef init_port
+#undef init_gatewayports
+#undef init_streamlocalbindmask
+#undef init_streamlocalbindunlink
+#undef init_maxstartups
+#undef init_permituserenv
+#undef init_persourcenetblocksize
+#undef init_persourcepenalties
+#undef init_rekeylimit
+#undef init_subsystem
+#undef init_timingsecret
+#undef SSHCONF_INT
+#undef SSHCONF_INTFLAG
+#undef SSHCONF_STRING
+#undef SSHCONF_STRARRAY
+#undef SSHCONF_CUSTOM
+#undef SSHCONF_NONCONF
+#undef SSHCONF_NOSUPPORT
+#undef SSHCONF_ALIAS
 }
 
 /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
@@ -293,15 +266,34 @@ fill_default_server_options(ServerOptions *options)
 {
        u_int i;
 
-       /* Portable-specific options */
-       if (options->use_pam == -1)
-               options->use_pam = 0;
-       if (options->pam_service_name == NULL)
-               options->pam_service_name = xstrdup(SSHD_PAM_SERVICE);
+#define SSHCONF_INT(var, conf, flags, ms, def, cp) \
+       if (options->var == -1) \
+               options->var = def;
+#define SSHCONF_INTFLAG(var, conf, flags, def, cp) \
+       if (options->var == -1) \
+               options->var = def;
+#define SSHCONF_STRING(var, conf, flags, cp)           /* done manually */
+#define SSHCONF_STRARRAY(var, nvar, conf, flags, cp)   /* done manually */
+#define SSHCONF_CUSTOM(conf, funcsuffix, flags, cp)    /* done manually */
+#define SSHCONF_NONCONF(funcsuffix)                    /* done manually */
+#define SSHCONF_NOSUPPORT(var, conf, opcode, flags)    /* empty */
+#define SSHCONF_ALIAS(old, conf, flags)                        /* empty */
+
+       /* XXX maybe use macros here too to force consistency? */
+
+       SSHD_CONFIG_ENTRIES
+
+#undef SSHCONF_INT
+#undef SSHCONF_INTFLAG
+#undef SSHCONF_STRING
+#undef SSHCONF_STRARRAY
+#undef SSHCONF_CUSTOM
+#undef SSHCONF_NONCONF
+#undef SSHCONF_NOSUPPORT
+#undef SSHCONF_ALIAS
 
-       /* Standard Options */
        if (options->num_host_key_files == 0) {
-               /* fill default hostkeys for protocols */
+               /* fill default hostkeys */
                servconf_add_hostkey("[default]", 0, options,
                    _PATH_HOST_RSA_KEY_FILE, 0);
 #ifdef OPENSSL_HAS_ECC
@@ -314,97 +306,26 @@ fill_default_server_options(ServerOptions *options)
        /* No certificates by default */
        if (options->num_ports == 0)
                options->ports[options->num_ports++] = SSH_DEFAULT_PORT;
-       if (options->address_family == -1)
-               options->address_family = AF_UNSPEC;
        if (options->listen_addrs == NULL)
                add_listen_addr(options, NULL, NULL, 0);
        if (options->pid_file == NULL)
                options->pid_file = xstrdup(_PATH_SSH_DAEMON_PID_FILE);
        if (options->moduli_file == NULL)
                options->moduli_file = xstrdup(_PATH_DH_MODULI);
-       if (options->login_grace_time == -1)
-               options->login_grace_time = 120;
-       if (options->permit_root_login == PERMIT_NOT_SET)
-               options->permit_root_login = PERMIT_NO_PASSWD;
-       if (options->ignore_rhosts == -1)
-               options->ignore_rhosts = 1;
-       if (options->ignore_user_known_hosts == -1)
-               options->ignore_user_known_hosts = 0;
-       if (options->print_motd == -1)
-               options->print_motd = 1;
-       if (options->print_lastlog == -1)
-               options->print_lastlog = 1;
-       if (options->x11_forwarding == -1)
-               options->x11_forwarding = 0;
-       if (options->x11_display_offset == -1)
-               options->x11_display_offset = 10;
-       if (options->x11_use_localhost == -1)
-               options->x11_use_localhost = 1;
        if (options->xauth_location == NULL)
                options->xauth_location = xstrdup(_PATH_XAUTH);
-       if (options->permit_tty == -1)
-               options->permit_tty = 1;
-       if (options->permit_user_rc == -1)
-               options->permit_user_rc = 1;
-       if (options->strict_modes == -1)
-               options->strict_modes = 1;
-       if (options->tcp_keep_alive == -1)
-               options->tcp_keep_alive = 1;
        if (options->log_facility == SYSLOG_FACILITY_NOT_SET)
                options->log_facility = SYSLOG_FACILITY_AUTH;
        if (options->log_level == SYSLOG_LEVEL_NOT_SET)
                options->log_level = SYSLOG_LEVEL_INFO;
-       if (options->hostbased_authentication == -1)
-               options->hostbased_authentication = 0;
-       if (options->hostbased_uses_name_from_packet_only == -1)
-               options->hostbased_uses_name_from_packet_only = 0;
-       if (options->pubkey_authentication == -1)
-               options->pubkey_authentication = 1;
-       if (options->pubkey_auth_options == -1)
-               options->pubkey_auth_options = 0;
-       if (options->kerberos_authentication == -1)
-               options->kerberos_authentication = 0;
-       if (options->kerberos_or_local_passwd == -1)
-               options->kerberos_or_local_passwd = 1;
-       if (options->kerberos_ticket_cleanup == -1)
-               options->kerberos_ticket_cleanup = 1;
-       if (options->kerberos_get_afs_token == -1)
-               options->kerberos_get_afs_token = 0;
-       if (options->gss_authentication == -1)
-               options->gss_authentication = 0;
-       if (options->gss_cleanup_creds == -1)
-               options->gss_cleanup_creds = 1;
-       if (options->gss_deleg_creds == -1)
-               options->gss_deleg_creds = 1;
-       if (options->gss_strict_acceptor == -1)
-               options->gss_strict_acceptor = 1;
-       if (options->password_authentication == -1)
-               options->password_authentication = 1;
-       if (options->kbd_interactive_authentication == -1)
-               options->kbd_interactive_authentication = 1;
-       if (options->permit_empty_passwd == -1)
-               options->permit_empty_passwd = 0;
        if (options->permit_user_env == -1) {
                options->permit_user_env = 0;
                options->permit_user_env_allowlist = NULL;
        }
-       if (options->compression == -1)
-#ifdef WITH_ZLIB
-               options->compression = COMP_DELAYED;
-#else
-               options->compression = COMP_NONE;
-#endif
-
        if (options->rekey_limit == -1)
                options->rekey_limit = 0;
        if (options->rekey_interval == -1)
                options->rekey_interval = 0;
-       if (options->allow_tcp_forwarding == -1)
-               options->allow_tcp_forwarding = FORWARD_ALLOW;
-       if (options->allow_streamlocal_forwarding == -1)
-               options->allow_streamlocal_forwarding = FORWARD_ALLOW;
-       if (options->allow_agent_forwarding == -1)
-               options->allow_agent_forwarding = 1;
        if (options->fwd_opts.gateway_ports == -1)
                options->fwd_opts.gateway_ports = 0;
        if (options->max_startups == -1)
@@ -413,8 +334,6 @@ fill_default_server_options(ServerOptions *options)
                options->max_startups_rate = 30;                /* 30% */
        if (options->max_startups_begin == -1)
                options->max_startups_begin = 10;
-       if (options->per_source_max_startups == -1)
-               options->per_source_max_startups = INT_MAX;
        if (options->per_source_masklen_ipv4 == -1)
                options->per_source_masklen_ipv4 = 32;
        if (options->per_source_masklen_ipv6 == -1)
@@ -445,16 +364,6 @@ fill_default_server_options(ServerOptions *options)
                options->per_source_penalty.penalty_min = 15.0;
        if (options->per_source_penalty.penalty_max < 0.0)
                options->per_source_penalty.penalty_max = 600.0;
-       if (options->max_authtries == -1)
-               options->max_authtries = DEFAULT_AUTH_FAIL_MAX;
-       if (options->max_sessions == -1)
-               options->max_sessions = DEFAULT_SESSIONS_MAX;
-       if (options->use_dns == -1)
-               options->use_dns = 0;
-       if (options->client_alive_interval == -1)
-               options->client_alive_interval = 0;
-       if (options->client_alive_count_max == -1)
-               options->client_alive_count_max = 3;
        if (options->num_authkeys_files == 0) {
                opt_array_append("[default]", 0, "AuthorizedKeysFiles",
                    &options->authorized_keys_files,
@@ -465,8 +374,6 @@ fill_default_server_options(ServerOptions *options)
                    &options->num_authkeys_files,
                    _PATH_SSH_USER_PERMITTED_KEYS2);
        }
-       if (options->permit_tun == -1)
-               options->permit_tun = SSH_TUNMODE_NO;
        if (options->ip_qos_interactive == -1)
                options->ip_qos_interactive = IPTOS_DSCP_EF;
        if (options->ip_qos_bulk == -1)
@@ -477,24 +384,12 @@ fill_default_server_options(ServerOptions *options)
                options->fwd_opts.streamlocal_bind_mask = 0177;
        if (options->fwd_opts.streamlocal_bind_unlink == -1)
                options->fwd_opts.streamlocal_bind_unlink = 0;
-       if (options->fingerprint_hash == -1)
-               options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
-       if (options->disable_forwarding == -1)
-               options->disable_forwarding = 0;
-       if (options->expose_userauth_info == -1)
-               options->expose_userauth_info = 0;
        if (options->sk_provider == NULL)
                options->sk_provider = xstrdup("internal");
-       if (options->required_rsa_size == -1)
-               options->required_rsa_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
-       if (options->unused_connection_timeout == -1)
-               options->unused_connection_timeout = 0;
        if (options->sshd_session_path == NULL)
                options->sshd_session_path = xstrdup(_PATH_SSHD_SESSION);
        if (options->sshd_auth_path == NULL)
                options->sshd_auth_path = xstrdup(_PATH_SSHD_AUTH);
-       if (options->refuse_connection == -1)
-               options->refuse_connection = 0;
 
        assemble_algorithms(options);
 
@@ -540,47 +435,31 @@ fill_default_server_options(ServerOptions *options)
 #undef CLEAR_ON_NONE_ARRAY
 }
 
+/* Macros to declare ServerOpCodes enum values */
+#define SSHCONF_INT(var, conf, flags, ms, def, cp)     s##conf,
+#define SSHCONF_INTFLAG(var, conf, flags, def, cp)     s##conf,
+#define SSHCONF_STRING(var, conf, flags, cp)           s##conf,
+#define SSHCONF_STRARRAY(var, nvar, conf, flags, cp)   s##conf,
+#define SSHCONF_CUSTOM(conf, funcsuffix, flags, cp)    s##conf,
+#define SSHCONF_NONCONF(funcsuffix)                    /* empty */
+#define SSHCONF_NOSUPPORT(var, conf, opcode, flags)    /* empty */
+#define SSHCONF_ALIAS(old, conf, flags)                        /* empty */
+
 /* Keyword tokens. */
 typedef enum {
        sBadOption,             /* == unknown option */
-       /* Portable-specific options */
-       sUsePAM, sPAMServiceName,
-       /* Standard Options */
-       sPort, sHostKeyFile, sLoginGraceTime,
-       sPermitRootLogin, sLogFacility, sLogLevel, sLogVerbose,
-       sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup,
-       sKerberosGetAFSToken, sPasswordAuthentication,
-       sKbdInteractiveAuthentication, sListenAddress, sAddressFamily,
-       sPrintMotd, sPrintLastLog, sIgnoreRhosts,
-       sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost,
-       sPermitTTY, sStrictModes, sEmptyPasswd, sTCPKeepAlive,
-       sPermitUserEnvironment, sAllowTcpForwarding, sCompression,
-       sRekeyLimit, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
-       sIgnoreUserKnownHosts, sCiphers, sMacs, sPidFile, sModuliFile,
-       sGatewayPorts, sPubkeyAuthentication, sPubkeyAcceptedAlgorithms,
-       sXAuthLocation, sSubsystem, sMaxStartups, sMaxAuthTries, sMaxSessions,
-       sBanner, sUseDNS, sHostbasedAuthentication,
-       sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedAlgorithms,
-       sHostKeyAlgorithms, sPerSourceMaxStartups, sPerSourceNetBlockSize,
-       sPerSourcePenalties, sPerSourcePenaltyExemptList,
-       sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
-       sGssAuthentication, sGssCleanupCreds, sGssDelegateCreds, sGssStrictAcceptor,
-       sAcceptEnv, sSetEnv, sPermitTunnel,
-       sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
-       sUsePrivilegeSeparation, sAllowAgentForwarding,
-       sHostCertificate, sInclude,
-       sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
-       sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser,
-       sKexAlgorithms, sCASignatureAlgorithms, sIPQoS, sVersionAddendum,
-       sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
-       sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
-       sStreamLocalBindMask, sStreamLocalBindUnlink,
-       sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
-       sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
-       sRequiredRSASize, sChannelTimeout, sUnusedConnectionTimeout,
-       sSshdSessionPath, sSshdAuthPath, sRefuseConnection,
+       SSHD_CONFIG_ENTRIES
+       sMatch, sInclude,
        sDeprecated, sIgnore, sUnsupported
 } ServerOpCodes;
+#undef SSHCONF_INT
+#undef SSHCONF_INTFLAG
+#undef SSHCONF_STRING
+#undef SSHCONF_STRARRAY
+#undef SSHCONF_CUSTOM
+#undef SSHCONF_NONCONF
+#undef SSHCONF_NOSUPPORT
+#undef SSHCONF_ALIAS
 
 #define SSHCFG_GLOBAL          0x01    /* allowed in main section of config */
 #define SSHCFG_MATCH           0x02    /* allowed inside a Match section */
@@ -588,170 +467,44 @@ typedef enum {
 #define SSHCFG_NEVERMATCH      0x04  /* Match never matches; internal only */
 #define SSHCFG_MATCH_ONLY      0x08  /* Match only in conditional blocks; internal only */
 
+/* Macros to define keywords[] entries */
+#define SSHCONF_KW(conf, flags)                { #conf, s##conf, flags },
+#define SSHCONF_INT(var, conf, flags, ms, def, cp)     SSHCONF_KW(conf, flags)
+#define SSHCONF_INTFLAG(var, conf, flags, def, cp)     SSHCONF_KW(conf, flags)
+#define SSHCONF_STRING(var, conf, flags, cp)           SSHCONF_KW(conf, flags)
+#define SSHCONF_STRARRAY(var, nvar, conf, flags, cp)   SSHCONF_KW(conf, flags)
+#define SSHCONF_CUSTOM(conf, funcsuffix, flags, cp)    SSHCONF_KW(conf, flags)
+#define SSHCONF_NONCONF(funcsuffix)                    /* empty */
+#define SSHCONF_DEPRECATED                             sDeprecated
+#define SSHCONF_IGNORE                                 sIgnore
+#define SSHCONF_UNSUPPORTED                            sUnsupported
+#define SSHCONF_NOSUPPORT(var, conf, opcode, flags) \
+       { #conf, opcode, flags },
+#define SSHCONF_ALIAS(old, conf, flags) \
+       { #old, s##conf, flags },
+
 /* Textual representation of the tokens. */
 static struct {
        const char *name;
        ServerOpCodes opcode;
        u_int flags;
 } keywords[] = {
-       /* Portable-specific options */
-#ifdef USE_PAM
-       { "usepam", sUsePAM, SSHCFG_GLOBAL },
-       { "pamservicename", sPAMServiceName, SSHCFG_ALL },
-#else
-       { "usepam", sUnsupported, SSHCFG_GLOBAL },
-       { "pamservicename", sUnsupported, SSHCFG_ALL },
-#endif
-       { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL },
-       /* Standard Options */
-       { "port", sPort, SSHCFG_GLOBAL },
-       { "hostkey", sHostKeyFile, SSHCFG_GLOBAL },
-       { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL },          /* alias */
-       { "hostkeyagent", sHostKeyAgent, SSHCFG_GLOBAL },
-       { "pidfile", sPidFile, SSHCFG_GLOBAL },
-       { "modulifile", sModuliFile, SSHCFG_GLOBAL },
-       { "serverkeybits", sDeprecated, SSHCFG_GLOBAL },
-       { "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL },
-       { "keyregenerationinterval", sDeprecated, SSHCFG_GLOBAL },
-       { "permitrootlogin", sPermitRootLogin, SSHCFG_ALL },
-       { "syslogfacility", sLogFacility, SSHCFG_GLOBAL },
-       { "loglevel", sLogLevel, SSHCFG_ALL },
-       { "logverbose", sLogVerbose, SSHCFG_ALL },
-       { "rhostsauthentication", sDeprecated, SSHCFG_GLOBAL },
-       { "rhostsrsaauthentication", sDeprecated, SSHCFG_ALL },
-       { "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL },
-       { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_ALL },
-       { "hostbasedacceptedalgorithms", sHostbasedAcceptedAlgorithms, SSHCFG_ALL },
-       { "hostbasedacceptedkeytypes", sHostbasedAcceptedAlgorithms, SSHCFG_ALL }, /* obsolete */
-       { "hostkeyalgorithms", sHostKeyAlgorithms, SSHCFG_GLOBAL },
-       { "rsaauthentication", sDeprecated, SSHCFG_ALL },
-       { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL },
-       { "pubkeyacceptedalgorithms", sPubkeyAcceptedAlgorithms, SSHCFG_ALL },
-       { "pubkeyacceptedkeytypes", sPubkeyAcceptedAlgorithms, SSHCFG_ALL }, /* obsolete */
-       { "pubkeyauthoptions", sPubkeyAuthOptions, SSHCFG_ALL },
-       { "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */
-#ifdef KRB5
-       { "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL },
-       { "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_GLOBAL },
-       { "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_GLOBAL },
-#ifdef USE_AFS
-       { "kerberosgetafstoken", sKerberosGetAFSToken, SSHCFG_GLOBAL },
-#else
-       { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL },
-#endif
-#else
-       { "kerberosauthentication", sUnsupported, SSHCFG_ALL },
-       { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL },
-       { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL },
-       { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL },
-#endif
-       { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL },
-       { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL },
-#ifdef GSSAPI
-       { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
-       { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
-       { "gssapidelegatecredentials", sGssDelegateCreds, SSHCFG_GLOBAL },
-       { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
-#else
-       { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
-       { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
-       { "gssapidelegatecredentials", sUnsupported, SSHCFG_GLOBAL },
-       { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
-#endif
-       { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
-       { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
-       { "challengeresponseauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, /* alias */
-       { "skeyauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, /* alias */
-       { "checkmail", sDeprecated, SSHCFG_GLOBAL },
-       { "listenaddress", sListenAddress, SSHCFG_GLOBAL },
-       { "addressfamily", sAddressFamily, SSHCFG_GLOBAL },
-       { "printmotd", sPrintMotd, SSHCFG_GLOBAL },
-#ifdef DISABLE_LASTLOG
-       { "printlastlog", sUnsupported, SSHCFG_GLOBAL },
-#else
-       { "printlastlog", sPrintLastLog, SSHCFG_GLOBAL },
-#endif
-       { "ignorerhosts", sIgnoreRhosts, SSHCFG_ALL },
-       { "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL },
-       { "x11forwarding", sX11Forwarding, SSHCFG_ALL },
-       { "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL },
-       { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL },
-       { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL },
-       { "strictmodes", sStrictModes, SSHCFG_GLOBAL },
-       { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL },
-       { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL },
-       { "uselogin", sDeprecated, SSHCFG_GLOBAL },
-       { "compression", sCompression, SSHCFG_GLOBAL },
-       { "rekeylimit", sRekeyLimit, SSHCFG_ALL },
-       { "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL },
-       { "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL },  /* obsolete alias */
-       { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL },
-       { "allowagentforwarding", sAllowAgentForwarding, SSHCFG_ALL },
-       { "allowusers", sAllowUsers, SSHCFG_ALL },
-       { "denyusers", sDenyUsers, SSHCFG_ALL },
-       { "allowgroups", sAllowGroups, SSHCFG_ALL },
-       { "denygroups", sDenyGroups, SSHCFG_ALL },
-       { "ciphers", sCiphers, SSHCFG_GLOBAL },
-       { "macs", sMacs, SSHCFG_GLOBAL },
-       { "protocol", sIgnore, SSHCFG_GLOBAL },
-       { "gatewayports", sGatewayPorts, SSHCFG_ALL },
-       { "subsystem", sSubsystem, SSHCFG_ALL },
-       { "maxstartups", sMaxStartups, SSHCFG_GLOBAL },
-       { "persourcemaxstartups", sPerSourceMaxStartups, SSHCFG_GLOBAL },
-       { "persourcenetblocksize", sPerSourceNetBlockSize, SSHCFG_GLOBAL },
-       { "persourcepenalties", sPerSourcePenalties, SSHCFG_GLOBAL },
-       { "persourcepenaltyexemptlist", sPerSourcePenaltyExemptList, SSHCFG_GLOBAL },
-       { "maxauthtries", sMaxAuthTries, SSHCFG_ALL },
-       { "maxsessions", sMaxSessions, SSHCFG_ALL },
-       { "banner", sBanner, SSHCFG_ALL },
-       { "usedns", sUseDNS, SSHCFG_GLOBAL },
-       { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL },
-       { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL },
-       { "clientaliveinterval", sClientAliveInterval, SSHCFG_ALL },
-       { "clientalivecountmax", sClientAliveCountMax, SSHCFG_ALL },
-       { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL },
-       { "authorizedkeysfile2", sDeprecated, SSHCFG_ALL },
-       { "useprivilegeseparation", sDeprecated, SSHCFG_GLOBAL},
-       { "acceptenv", sAcceptEnv, SSHCFG_ALL },
-       { "setenv", sSetEnv, SSHCFG_ALL },
-       { "permittunnel", sPermitTunnel, SSHCFG_ALL },
-       { "permittty", sPermitTTY, SSHCFG_ALL },
-       { "permituserrc", sPermitUserRC, SSHCFG_ALL },
+       SSHD_CONFIG_ENTRIES
        { "match", sMatch, SSHCFG_ALL },
-       { "permitopen", sPermitOpen, SSHCFG_ALL },
-       { "permitlisten", sPermitListen, SSHCFG_ALL },
-       { "forcecommand", sForceCommand, SSHCFG_ALL },
-       { "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
-       { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL },
-       { "revokedkeys", sRevokedKeys, SSHCFG_ALL },
-       { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
-       { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL },
-       { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
        { "include", sInclude, SSHCFG_ALL },
-       { "ipqos", sIPQoS, SSHCFG_ALL },
-       { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
-       { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
-       { "authorizedprincipalscommand", sAuthorizedPrincipalsCommand, SSHCFG_ALL },
-       { "authorizedprincipalscommanduser", sAuthorizedPrincipalsCommandUser, SSHCFG_ALL },
-       { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
-       { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL },
-       { "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL },
-       { "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL },
-       { "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL },
-       { "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL },
-       { "disableforwarding", sDisableForwarding, SSHCFG_ALL },
-       { "exposeauthinfo", sExposeAuthInfo, SSHCFG_ALL },
-       { "rdomain", sRDomain, SSHCFG_ALL },
-       { "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL },
-       { "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL },
-       { "requiredrsasize", sRequiredRSASize, SSHCFG_ALL },
-       { "channeltimeout", sChannelTimeout, SSHCFG_ALL },
-       { "unusedconnectiontimeout", sUnusedConnectionTimeout, SSHCFG_ALL },
-       { "sshdsessionpath", sSshdSessionPath, SSHCFG_GLOBAL },
-       { "sshdauthpath", sSshdAuthPath, SSHCFG_GLOBAL },
-       { "refuseconnection", sRefuseConnection, SSHCFG_ALL },
        { NULL, sBadOption, 0 }
 };
+#undef SSHCONF_INT
+#undef SSHCONF_INTFLAG
+#undef SSHCONF_STRING
+#undef SSHCONF_STRARRAY
+#undef SSHCONF_CUSTOM
+#undef SSHCONF_NONCONF
+#undef SSHCONF_DEPRECATED
+#undef SSHCONF_IGNORE
+#undef SSHCONF_UNSUPPORTED
+#undef SSHCONF_NOSUPPORT
+#undef SSHCONF_ALIAS
 
 static struct {
        int val;
@@ -1044,12 +797,13 @@ match_cfg_line(const char *full_line, int *acp, char ***avp,
                    full_line, line);
        } else {
                debug3("checking match for '%s' user %s%s host %s addr %s "
-                   "laddr %s lport %d on line %d", full_line,
+                   "laddr %s lport %d rdomain %s on line %d", full_line,
                    ci->user ? ci->user : "(null)",
                    ci->user_invalid ? " (invalid)" : "",
                    ci->host ? ci->host : "(null)",
                    ci->address ? ci->address : "(null)",
-                   ci->laddress ? ci->laddress : "(null)", ci->lport, line);
+                   ci->laddress ? ci->laddress : "(null)", ci->lport,
+                   ci->rdomain ? ci->rdomain : "(null)", line);
        }
 
        while ((oattrib = argv_next(acp, avp)) != NULL) {
@@ -1389,6 +1143,7 @@ process_server_config_line_depth(ServerOptions *options, char *line,
 
        switch (opcode) {
        /* Portable-specific options */
+#ifdef WITH_PAM
        case sUsePAM:
                intptr = &options->use_pam;
                goto parse_flag;
@@ -1402,6 +1157,7 @@ process_server_config_line_depth(ServerOptions *options, char *line,
                if (*activep && *charptr == NULL)
                        *charptr = xstrdup(arg);
                break;
+#endif
 
        /* Standard Options */
        case sBadOption:
@@ -1499,7 +1255,7 @@ process_server_config_line_depth(ServerOptions *options, char *line,
                        *intptr = value;
                break;
 
-       case sHostKeyFile:
+       case sHostKey:
                arg = argv_next(&ac, &av);
                if (!arg || *arg == '\0')
                        fatal("%s line %d: missing file name.",
@@ -1630,6 +1386,7 @@ process_server_config_line_depth(ServerOptions *options, char *line,
                        *intptr = value;
                break;
 
+#ifdef KRB5
        case sKerberosAuthentication:
                intptr = &options->kerberos_authentication;
                goto parse_flag;
@@ -1641,11 +1398,14 @@ process_server_config_line_depth(ServerOptions *options, char *line,
        case sKerberosTicketCleanup:
                intptr = &options->kerberos_ticket_cleanup;
                goto parse_flag;
-
+#ifdef USE_AFS
        case sKerberosGetAFSToken:
                intptr = &options->kerberos_get_afs_token;
                goto parse_flag;
+#endif /* USE_AFS */
+#endif /* KRB5 */
 
+#ifdef GSSAPI
        case sGssAuthentication:
                intptr = &options->gss_authentication;
                goto parse_flag;
@@ -1661,6 +1421,7 @@ process_server_config_line_depth(ServerOptions *options, char *line,
        case sGssStrictAcceptor:
                intptr = &options->gss_strict_acceptor;
                goto parse_flag;
+#endif /* GSSAPI */
 
        case sPasswordAuthentication:
                intptr = &options->password_authentication;
@@ -1674,9 +1435,11 @@ process_server_config_line_depth(ServerOptions *options, char *line,
                intptr = &options->print_motd;
                goto parse_flag;
 
+#ifndef DISABLE_LASTLOG
        case sPrintLastLog:
                intptr = &options->print_lastlog;
                goto parse_flag;
+#endif
 
        case sX11Forwarding:
                intptr = &options->x11_forwarding;
@@ -1717,7 +1480,7 @@ process_server_config_line_depth(ServerOptions *options, char *line,
                intptr = &options->tcp_keep_alive;
                goto parse_flag;
 
-       case sEmptyPasswd:
+       case sPermitEmptyPasswords:
                intptr = &options->permit_empty_passwd;
                goto parse_flag;
 
@@ -1789,7 +1552,7 @@ process_server_config_line_depth(ServerOptions *options, char *line,
                intptr = &options->use_dns;
                goto parse_flag;
 
-       case sLogFacility:
+       case sSyslogFacility:
                log_facility_ptr = &options->log_facility;
                arg = argv_next(&ac, &av);
                value = log_facility_number(arg);
@@ -2854,6 +2617,7 @@ parse_server_match_config(ServerOptions *options,
        parse_server_config(&mo, "reprocess config", cfg, includes,
            connectinfo, 0);
        copy_set_server_options(options, &mo, 0);
+       free_server_options(&mo);
 }
 
 int
@@ -2932,122 +2696,1230 @@ servconf_merge_subsystems(ServerOptions *dst, ServerOptions *src)
        }
 }
 
-/*
- * Copy any supported values that are set.
- *
- * If the preauth flag is set, we do not bother copying the string or
- * array values that are not used pre-authentication, because any that we
- * do use must be explicitly sent in mm_getpwnamallow().
- */
-void
-copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
+static int
+serialise_s32(struct sshbuf *buf, int v)
+{
+       uint32_t uv;
+       int r;
+
+       uv = v < 0 ? (uint32_t)(-(v + 1)) + 1 : (uint32_t)v;
+       if ((r = sshbuf_put_u8(buf, v < 0)) != 0 ||
+           (r = sshbuf_put_u32(buf, uv)) != 0)
+               return r;
+       return 0;
+}
+
+static int
+serialise_s64(struct sshbuf *buf, int64_t v)
+{
+       uint64_t uv;
+       int r;
+
+       uv = v < 0 ? (uint64_t)(-(v + 1)) + 1 : (uint64_t)v;
+       if ((r = sshbuf_put_u8(buf, v < 0)) != 0 ||
+           (r = sshbuf_put_u64(buf, uv)) != 0)
+               return r;
+       return 0;
+}
+
+static int
+serialise_mode(struct sshbuf *buf, mode_t v)
 {
-#define M_CP_INTOPT(n) do {\
-       if (src->n != -1) \
-               dst->n = src->n; \
-} while (0)
-
-       M_CP_INTOPT(password_authentication);
-       M_CP_INTOPT(gss_authentication);
-       M_CP_INTOPT(pubkey_authentication);
-       M_CP_INTOPT(pubkey_auth_options);
-       M_CP_INTOPT(kerberos_authentication);
-       M_CP_INTOPT(hostbased_authentication);
-       M_CP_INTOPT(hostbased_uses_name_from_packet_only);
-       M_CP_INTOPT(kbd_interactive_authentication);
-       M_CP_INTOPT(permit_root_login);
-       M_CP_INTOPT(permit_empty_passwd);
-       M_CP_INTOPT(ignore_rhosts);
-
-       M_CP_INTOPT(allow_tcp_forwarding);
-       M_CP_INTOPT(allow_streamlocal_forwarding);
-       M_CP_INTOPT(allow_agent_forwarding);
-       M_CP_INTOPT(disable_forwarding);
-       M_CP_INTOPT(expose_userauth_info);
-       M_CP_INTOPT(permit_tun);
-       M_CP_INTOPT(fwd_opts.gateway_ports);
-       M_CP_INTOPT(fwd_opts.streamlocal_bind_unlink);
-       M_CP_INTOPT(x11_display_offset);
-       M_CP_INTOPT(x11_forwarding);
-       M_CP_INTOPT(x11_use_localhost);
-       M_CP_INTOPT(permit_tty);
-       M_CP_INTOPT(permit_user_rc);
-       M_CP_INTOPT(max_sessions);
-       M_CP_INTOPT(max_authtries);
-       M_CP_INTOPT(client_alive_count_max);
-       M_CP_INTOPT(client_alive_interval);
-       M_CP_INTOPT(ip_qos_interactive);
-       M_CP_INTOPT(ip_qos_bulk);
-       M_CP_INTOPT(rekey_limit);
-       M_CP_INTOPT(rekey_interval);
-       M_CP_INTOPT(log_level);
-       M_CP_INTOPT(required_rsa_size);
-       M_CP_INTOPT(unused_connection_timeout);
-       M_CP_INTOPT(refuse_connection);
+       u_int uv;
+
+       if (v == (mode_t)-1)
+               return serialise_s32(buf, -1);
+       uv = (u_int)v;
+       if ((mode_t)uv != v || uv > 0777)
+               return SSH_ERR_INVALID_FORMAT;
+       return serialise_s32(buf, (int)uv);
+}
 
+static int
+serialise_double(struct sshbuf *buf, double v)
+{
        /*
-        * The bind_mask is a mode_t that may be unsigned, so we can't use
-        * M_CP_INTOPT - it does a signed comparison that causes compiler
-        * warnings.
+        * XXX this is no good for a wire encoding.
+        * It's fine for passing configurations via RPC, but it would
+        * be nicer to have an exact binary encoding here.
         */
-       if (src->fwd_opts.streamlocal_bind_mask != (mode_t)-1) {
-               dst->fwd_opts.streamlocal_bind_mask =
-                   src->fwd_opts.streamlocal_bind_mask;
+       return sshbuf_put(buf, &v, sizeof(v));
+}
+
+static int
+serialise_nullable_string(struct sshbuf *buf, const char *s)
+{
+       int r;
+
+       if ((r = sshbuf_put_u8(buf, s != NULL)) != 0)
+               return r;
+       if (s == NULL)
+               return 0;
+       return sshbuf_put_cstring(buf, s);
+}
+
+static int
+serialise_nullable_string_array(struct sshbuf *buf, char **a, u_int n)
+{
+       int r;
+       u_int i;
+
+       if ((r = sshbuf_put_u32(buf, n)) != 0)
+               return r;
+       for (i = 0; i < n; i++) {
+               if ((r = serialise_nullable_string(buf, a[i])) != 0)
+                       return r;
        }
+       return 0;
+}
 
-       /* M_CP_STROPT and M_CP_STRARRAYOPT should not appear before here */
-#define M_CP_STROPT(n) do {\
-       if (src->n != NULL && dst->n != src->n) { \
-               free(dst->n); \
-               dst->n = xstrdup(src->n); \
-       } \
-} while(0)
-#define M_CP_STRARRAYOPT(s, num_s, clobber) do {\
-       u_int i; \
-       if (src->num_s != 0) { \
-               for (i = 0; i < dst->num_s; i++) \
-                       free(dst->s[i]); \
-               free(dst->s); \
-               dst->s = xcalloc(src->num_s, sizeof(*dst->s)); \
-               for (i = 0; i < src->num_s; i++) \
-                       dst->s[i] = xstrdup(src->s[i]); \
-               if (clobber) \
-                       dst->num_s = src->num_s; \
-       } \
-} while(0)
-
-       /* See comment in servconf.h */
-       COPY_MATCH_STRING_OPTS();
+static int
+serialise_hostkeyfile(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+       u_int i;
 
-       /* Arguments that accept '+...' need to be expanded */
-       assemble_algorithms(dst);
+       if ((r = sshbuf_put_u32(buf, options->num_host_key_files)) != 0) {
+               error_fr(r, "serialise length");
+               return r;
+       }
+       for (i = 0; i < options->num_host_key_files; i++) {
+               if ((r = serialise_s32(buf,
+                   options->host_key_file_userprovided[i])) != 0 ||
+                   (r = serialise_nullable_string(buf,
+                   options->host_key_files[i])) != 0) {
+                       error_fr(r, "serialise member");
+                       return r;
+               }
+       }
+       return 0;
+}
 
-       /*
-        * The only things that should be below this point are string options
-        * which are only used after authentication.
-        */
-       if (preauth)
-               return;
+static int
+serialise_ipqos(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
 
-       /* These options may be "none" to clear a global setting */
-       M_CP_STROPT(adm_forced_command);
-       if (option_clear_or_none(dst->adm_forced_command)) {
-               free(dst->adm_forced_command);
-               dst->adm_forced_command = NULL;
+       if ((r = serialise_s32(buf, options->ip_qos_interactive)) != 0 ||
+           (r = serialise_s32(buf, options->ip_qos_bulk)) != 0) {
+               error_fr(r, "serialise");
+               return r;
        }
-       M_CP_STROPT(chroot_directory);
-       if (option_clear_or_none(dst->chroot_directory)) {
-               free(dst->chroot_directory);
-               dst->chroot_directory = NULL;
+
+       return 0;
+}
+
+static int
+serialise_listenaddress(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+       u_int i;
+
+       /* Note: only serialises queued listen addresses */
+       if ((r = sshbuf_put_u32(buf, options->num_queued_listens)) != 0) {
+               error_fr(r, "serialise length");
+               return r;
+       }
+       for (i = 0; i < options->num_queued_listens; i++) {
+               const struct queued_listenaddr *qla =
+                   options->queued_listen_addrs + i;
+
+               if ((r = sshbuf_put_cstring(buf, qla->addr)) != 0 ||
+                   (r = serialise_s32(buf, qla->port)) != 0 ||
+                   (r = serialise_nullable_string(buf, qla->rdomain)) != 0) {
+                       error_fr(r, "serialise member");
+                       return r;
+               }
+       }
+       return 0;
+}
+
+static int
+serialise_logfacility(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = serialise_s32(buf, (int)options->log_facility)) != 0) {
+               error_fr(r, "serialise");
+               return r;
        }
 
-       /* Subsystems require merging. */
-       servconf_merge_subsystems(dst, src);
+       return 0;
+}
+
+static int
+serialise_loglevel(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = serialise_s32(buf, (int)options->log_level)) != 0) {
+               error_fr(r, "serialise");
+               return r;
+       }
+
+       return 0;
+}
+
+static int
+serialise_port(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+       u_int i;
+
+       if ((r = sshbuf_put_u32(buf, options->num_ports)) != 0) {
+               error_fr(r, "serialise length");
+               return r;
+       }
+       for (i = 0; i < options->num_ports; i++) {
+               if ((r = serialise_s32(buf, options->ports[i])) != 0) {
+                       error_fr(r, "serialise port");
+                       return r;
+               }
+       }
+       return 0;
+}
+
+static int
+serialise_gatewayports(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = serialise_s32(buf, options->fwd_opts.gateway_ports)) != 0) {
+               error_fr(r, "serialise");
+               return r;
+       }
+       return 0;
+}
+
+static int
+serialise_streamlocalbindmask(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = serialise_mode(buf,
+           options->fwd_opts.streamlocal_bind_mask)) != 0) {
+               error_fr(r, "serialise");
+               return r;
+       }
+       return 0;
+}
+
+static int
+serialise_streamlocalbindunlink(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = sshbuf_put_u8(buf,
+           (options->fwd_opts.streamlocal_bind_unlink != 0))) != 0) {
+               error_fr(r, "serialise");
+               return r;
+       }
+       return 0;
+}
+
+static int
+serialise_maxstartups(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = serialise_s32(buf, options->max_startups_begin)) != 0 ||
+           (r = serialise_s32(buf, options->max_startups_rate)) != 0 ||
+           (r = serialise_s32(buf, options->max_startups)) != 0) {
+               error_fr(r, "serialise");
+               return r;
+       }
+
+       return 0;
+}
+
+static int
+serialise_permituserenv(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = serialise_s32(buf, options->permit_user_env)) != 0 ||
+           (r = serialise_nullable_string(buf,
+           options->permit_user_env_allowlist)) != 0) {
+               error_fr(r, "serialise");
+               return r;
+       }
+
+       return 0;
+}
+
+static int
+serialise_persourcenetblocksize(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = serialise_s32(buf, options->per_source_masklen_ipv4)) != 0 ||
+           (r = serialise_s32(buf, options->per_source_masklen_ipv6)) != 0) {
+               error_fr(r, "serialise");
+               return r;
+       }
+
+       return 0;
+}
+
+static int
+serialise_persourcepenalties(const ServerOptions *options, struct sshbuf *buf)
+{
+       const struct per_source_penalty *psp = &options->per_source_penalty;
+       int r;
+
+       if ((r = serialise_s32(buf, psp->enabled)) != 0 ||
+           (r = serialise_s32(buf, psp->max_sources4)) != 0 ||
+           (r = serialise_s32(buf, psp->max_sources6)) != 0 ||
+           (r = serialise_s32(buf, psp->overflow_mode)) != 0 ||
+           (r = serialise_s32(buf, psp->overflow_mode6)) != 0 ||
+           (r = serialise_double(buf, psp->penalty_crash)) != 0 ||
+           (r = serialise_double(buf, psp->penalty_grace)) != 0 ||
+           (r = serialise_double(buf, psp->penalty_authfail)) != 0 ||
+           (r = serialise_double(buf, psp->penalty_invaliduser)) != 0 ||
+           (r = serialise_double(buf, psp->penalty_noauth)) != 0 ||
+           (r = serialise_double(buf, psp->penalty_refuseconnection)) != 0 ||
+           (r = serialise_double(buf, psp->penalty_max)) != 0 ||
+           (r = serialise_double(buf, psp->penalty_min)) != 0) {
+               error_fr(r, "serialise");
+               return r;
+       }
+
+       return 0;
+}
+
+static int
+serialise_rekeylimit(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = serialise_s64(buf, options->rekey_limit)) != 0 ||
+           (r = serialise_s32(buf, options->rekey_interval)) != 0) {
+               error_fr(r, "serialise");
+               return r;
+       }
+
+       return 0;
+}
+
+static int
+serialise_subsystem(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+       u_int i;
+
+       if ((r = sshbuf_put_u32(buf, options->num_subsystems)) != 0) {
+               error_fr(r, "serialise length");
+               return r;
+       }
+       for (i = 0; i < options->num_subsystems; i++) {
+               if ((r = sshbuf_put_cstring(buf,
+                   options->subsystem_name[i])) != 0 ||
+                   (r = sshbuf_put_cstring(buf,
+                   options->subsystem_command[i])) != 0 ||
+                   (r = sshbuf_put_cstring(buf,
+                   options->subsystem_args[i])) != 0) {
+                       error_fr(r, "serialise member");
+                       return r;
+               }
+       }
+       return 0;
+}
+
+static int
+serialise_timingsecret(const ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = sshbuf_put_u64(buf, options->timing_secret)) != 0) {
+               error_fr(r, "serialise");
+               return r;
+       }
+       return 0;
 }
 
-#undef M_CP_INTOPT
-#undef M_CP_STROPT
-#undef M_CP_STRARRAYOPT
+
+int
+serialise_server_options(const ServerOptions *options, struct sshbuf **bufp)
+{
+       struct sshbuf *buf = NULL;
+       int r = SSH_ERR_INTERNAL_ERROR;
+
+       *bufp = NULL;
+
+       if ((buf = sshbuf_new()) == NULL)
+               return SSH_ERR_ALLOC_FAIL;
+       if ((r = sshbuf_put_u32(buf, SSHD_CONFIG_BLOB_VERSION)) != 0) {
+               error_fr(r, "serialise version");
+               goto out;
+       }
+
+#define SSHCONF_INT(var, conf, flags, ms, def, cp) \
+       if ((r = serialise_s32(buf, options->var)) != 0) { \
+               error_fr(r, "serialise %s", #var); \
+               goto out; \
+       }
+#define SSHCONF_INTFLAG(var, conf, flags, def, cp) \
+       if ((r = serialise_s32(buf, options->var)) != 0) { \
+               error_fr(r, "serialise %s", #var); \
+               goto out; \
+       }
+#define SSHCONF_STRING(var, conf, flags, cp) \
+       if ((r = serialise_nullable_string(buf, options->var)) != 0) { \
+               error_fr(r, "serialise %s", #var); \
+               goto out; \
+       }
+#define SSHCONF_STRARRAY(var, nvar, conf, flags, cp) \
+       if ((r = serialise_nullable_string_array(buf, options->var, \
+           options->nvar)) != 0) { \
+               error_fr(r, "serialise %s", #var); \
+               goto out; \
+       }
+#define SSHCONF_CUSTOM(conf, funcsuffix, flags, cp) \
+       if ((r = serialise_##funcsuffix(options, buf)) != 0) \
+               goto out;
+#define SSHCONF_NONCONF(funcsuffix) \
+       if ((r = serialise_##funcsuffix(options, buf)) != 0) \
+               goto out;
+#define SSHCONF_NOSUPPORT(var, conf, opcode, flags)    /* empty */
+#define SSHCONF_ALIAS(old, conf, flags)                        /* empty */
+
+       SSHD_CONFIG_ENTRIES
+
+#undef SSHCONF_INT
+#undef SSHCONF_INTFLAG
+#undef SSHCONF_STRING
+#undef SSHCONF_STRARRAY
+#undef SSHCONF_CUSTOM
+#undef SSHCONF_NONCONF
+#undef SSHCONF_NOSUPPORT
+#undef SSHCONF_ALIAS
+
+       /* success */
+       r = 0;
+       *bufp = buf;
+       buf = NULL; /* transferred */
+ out:
+       sshbuf_free(buf);
+       return r;
+}
+
+static int
+deserialise_s32(struct sshbuf *buf, int *v)
+{
+       uint32_t tmp;
+       int r;
+       u_char was_signed;
+
+       if ((r = sshbuf_get_u8(buf, &was_signed)) != 0 ||
+           (r = sshbuf_get_u32(buf, &tmp)) != 0)
+               return r;
+       if (was_signed > 1)
+               return SSH_ERR_INVALID_FORMAT;
+       if (was_signed) {
+               if (tmp > (uint32_t)INT_MAX + 1)
+                       return SSH_ERR_INVALID_FORMAT;
+               *v = tmp == (uint32_t)INT_MAX + 1 ? INT_MIN : -(int)tmp;
+       } else {
+               if (tmp > INT_MAX)
+                       return SSH_ERR_INVALID_FORMAT;
+               *v = (int)tmp;
+       }
+       return 0;
+}
+
+static int
+deserialise_s64(struct sshbuf *buf, int64_t *v)
+{
+       uint64_t tmp;
+       int r;
+       u_char was_signed;
+
+       if ((r = sshbuf_get_u8(buf, &was_signed)) != 0 ||
+           (r = sshbuf_get_u64(buf, &tmp)) != 0)
+               return r;
+       if (was_signed > 1)
+               return SSH_ERR_INVALID_FORMAT;
+       if (was_signed) {
+               if (tmp > (uint64_t)INT64_MAX + 1)
+                       return SSH_ERR_INVALID_FORMAT;
+               *v = tmp == (uint64_t)INT64_MAX + 1 ?
+                   INT64_MIN : -(int64_t)tmp;
+       } else {
+               if (tmp > INT64_MAX)
+                       return SSH_ERR_INVALID_FORMAT;
+               *v = (int64_t)tmp;
+       }
+       return 0;
+}
+
+static int
+deserialise_mode(struct sshbuf *buf, mode_t *v)
+{
+       int r, tmp;
+
+       if ((r = deserialise_s32(buf, &tmp)) != 0)
+               return r;
+       if (tmp == -1) {
+               *v = (mode_t)-1;
+               return 0;
+       }
+       if (tmp < 0 || tmp > 0777)
+               return SSH_ERR_INVALID_FORMAT;
+       *v = (mode_t)tmp;
+       return 0;
+}
+
+static int
+deserialise_double(struct sshbuf *buf, double *v)
+{
+       return sshbuf_get(buf, v, sizeof(*v));
+}
+
+static int
+deserialise_nullable_string(struct sshbuf *buf, char **sp)
+{
+       int r;
+       u_char present;
+
+       if ((r = sshbuf_get_u8(buf, &present)) != 0)
+               return r;
+       if (present == 0) {
+               *sp = NULL;
+               return 0;
+       }
+       if (present != 1)
+               return SSH_ERR_INVALID_FORMAT;
+       return sshbuf_get_cstring(buf, sp, NULL);
+}
+
+static void
+free_string_array(char **a, u_int n)
+{
+       u_int i;
+
+       if (a == NULL)
+               return;
+       for (i = 0; i < n; i++)
+               free(a[i]);
+       free(a);
+}
+
+static int
+deserialise_count(struct sshbuf *buf, u_int *np, const char *what)
+{
+       int r;
+       uint32_t n;
+
+       if ((r = sshbuf_get_u32(buf, &n)) != 0) {
+               error_fr(r, "deserialise %s length", what);
+               return r;
+       }
+       if (n > UINT_MAX) {
+               error_f("bad number of %s", what);
+               return SSH_ERR_INVALID_FORMAT;
+       }
+       if (n > sshbuf_len(buf)) {
+               error_f("bad number of %s", what);
+               return SSH_ERR_INVALID_FORMAT;
+       }
+       *np = n;
+       return 0;
+}
+
+static int
+deserialise_nullable_string_array(struct sshbuf *buf, char ***arrayp,
+    u_int *np)
+{
+       char **a = NULL;
+       int r;
+       u_int i, n;
+
+       *arrayp = NULL;
+       *np = 0;
+       if ((r = deserialise_count(buf, &n, "strings")) != 0)
+               return r;
+       if (n > 0)
+               a = xcalloc(n, sizeof(*a));
+       for (i = 0; i < n; i++) {
+               if ((r = deserialise_nullable_string(buf, a + i)) != 0) {
+                       free_string_array(a, i + 1);
+                       return r;
+               }
+       }
+       *arrayp = a;
+       *np = n;
+       return 0;
+}
+
+static void
+free_queued_listen_addrs(struct queued_listenaddr *qla, u_int n)
+{
+       u_int i;
+
+       if (qla == NULL)
+               return;
+       for (i = 0; i < n; i++) {
+               free(qla[i].addr);
+               free(qla[i].rdomain);
+       }
+       free(qla);
+}
+
+static int
+deserialise_hostkeyfile(ServerOptions *options, struct sshbuf *buf)
+{
+       int r, *userprovided = NULL;
+       u_int i, n;
+       char **files = NULL;
+
+       if ((r = deserialise_count(buf, &n, "host key files")) != 0)
+               return r;
+       if (n > 0) {
+               userprovided = xcalloc(n, sizeof(*userprovided));
+               files = xcalloc(n, sizeof(*files));
+       }
+       for (i = 0; i < n; i++) {
+               if ((r = deserialise_s32(buf, userprovided + i)) != 0 ||
+                   (r = deserialise_nullable_string(buf, files + i)) != 0) {
+                       error_fr(r, "deserialise member");
+                       free_string_array(files, i + 1);
+                       free(userprovided);
+                       return r;
+               }
+       }
+       options->num_host_key_files = n;
+       options->host_key_file_userprovided = userprovided;
+       options->host_key_files = files;
+       return 0;
+}
+
+static int
+deserialise_ipqos(ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = deserialise_s32(buf, &options->ip_qos_interactive)) != 0 ||
+           (r = deserialise_s32(buf, &options->ip_qos_bulk)) != 0) {
+               error_fr(r, "deserialise");
+               return r;
+       }
+
+       return 0;
+}
+
+static int
+deserialise_listenaddress(ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+       u_int i, n;
+       struct queued_listenaddr *qla = NULL;
+
+       if ((r = deserialise_count(buf, &n, "listen addresses")) != 0)
+               return r;
+       if (n > 0)
+               qla = xcalloc(n, sizeof(*qla));
+       for (i = 0; i < n; i++) {
+               if ((r = sshbuf_get_cstring(buf, &qla[i].addr, NULL)) != 0 ||
+                   (r = deserialise_s32(buf, &qla[i].port)) != 0 ||
+                   (r = deserialise_nullable_string(buf,
+                   &qla[i].rdomain)) != 0) {
+                       error_fr(r, "deserialise member");
+                       free_queued_listen_addrs(qla, i + 1);
+                       return r;
+               }
+       }
+       options->num_queued_listens = n;
+       options->queued_listen_addrs = qla;
+       return 0;
+}
+
+static int
+deserialise_logfacility(ServerOptions *options, struct sshbuf *buf)
+{
+       int r, tmp;
+
+       if ((r = deserialise_s32(buf, &tmp)) != 0) {
+               error_fr(r, "deserialise");
+               return r;
+       }
+       if (tmp != SYSLOG_FACILITY_NOT_SET &&
+           log_facility_name((SyslogFacility)tmp) == NULL) {
+               error_f("bad syslog facility");
+               return SSH_ERR_INVALID_FORMAT;
+       }
+       options->log_facility = (SyslogFacility)tmp;
+
+       return 0;
+}
+
+static int
+deserialise_loglevel(ServerOptions *options, struct sshbuf *buf)
+{
+       int r, tmp;
+
+       if ((r = deserialise_s32(buf, &tmp)) != 0) {
+               error_fr(r, "deserialise");
+               return r;
+       }
+       if (tmp != SYSLOG_LEVEL_NOT_SET &&
+           log_level_name((LogLevel)tmp) == NULL) {
+               error_f("bad log level");
+               return SSH_ERR_INVALID_FORMAT;
+       }
+       options->log_level = (LogLevel)tmp;
+
+       return 0;
+}
+
+static int
+deserialise_port(ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+       u_int i, n;
+
+       if ((r = deserialise_count(buf, &n, "ports")) != 0)
+               return r;
+       if (n > MAX_PORTS) {
+               error_f("bad number of ports");
+               return SSH_ERR_INVALID_FORMAT;
+       }
+       options->num_ports = n;
+       memset(options->ports, 0, sizeof(options->ports));
+       for (i = 0; i < options->num_ports; i++) {
+               if ((r = deserialise_s32(buf, options->ports + i)) != 0) {
+                       error_fr(r, "deserialise port");
+                       return r;
+               }
+       }
+       return 0;
+}
+
+static int
+deserialise_gatewayports(ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = deserialise_s32(buf, &options->fwd_opts.gateway_ports)) != 0) {
+               error_fr(r, "deserialise");
+               return r;
+       }
+       return 0;
+}
+
+static int
+deserialise_streamlocalbindmask(ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = deserialise_mode(buf,
+           &options->fwd_opts.streamlocal_bind_mask)) != 0) {
+               error_fr(r, "deserialise");
+               return r;
+       }
+       return 0;
+}
+
+static int
+deserialise_streamlocalbindunlink(ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+       u_char tmp;
+
+       if ((r = sshbuf_get_u8(buf, &tmp)) != 0) {
+               error_fr(r, "deserialise");
+               return r;
+       }
+       if (tmp > 1) {
+               error_f("bad boolean");
+               return SSH_ERR_INVALID_FORMAT;
+       }
+       options->fwd_opts.streamlocal_bind_unlink = tmp;
+       return 0;
+}
+
+static int
+deserialise_maxstartups(ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = deserialise_s32(buf, &options->max_startups_begin)) != 0 ||
+           (r = deserialise_s32(buf, &options->max_startups_rate)) != 0 ||
+           (r = deserialise_s32(buf, &options->max_startups)) != 0) {
+               error_fr(r, "deserialise");
+               return r;
+       }
+
+       return 0;
+}
+
+static int
+deserialise_permituserenv(ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = deserialise_s32(buf, &options->permit_user_env)) != 0 ||
+           (r = deserialise_nullable_string(buf,
+           &options->permit_user_env_allowlist)) != 0) {
+               error_fr(r, "deserialise");
+               return r;
+       }
+       return 0;
+}
+
+static int
+deserialise_persourcenetblocksize(ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = deserialise_s32(buf,
+           &options->per_source_masklen_ipv4)) != 0 ||
+           (r = deserialise_s32(buf,
+           &options->per_source_masklen_ipv6)) != 0) {
+               error_fr(r, "deserialise");
+               return r;
+       }
+
+       return 0;
+}
+
+static int
+deserialise_persourcepenalties(ServerOptions *options, struct sshbuf *buf)
+{
+       struct per_source_penalty *psp = &options->per_source_penalty;
+       int r;
+
+       if ((r = deserialise_s32(buf, &psp->enabled)) != 0 ||
+           (r = deserialise_s32(buf, &psp->max_sources4)) != 0 ||
+           (r = deserialise_s32(buf, &psp->max_sources6)) != 0 ||
+           (r = deserialise_s32(buf, &psp->overflow_mode)) != 0 ||
+           (r = deserialise_s32(buf, &psp->overflow_mode6)) != 0 ||
+           (r = deserialise_double(buf, &psp->penalty_crash)) != 0 ||
+           (r = deserialise_double(buf, &psp->penalty_grace)) != 0 ||
+           (r = deserialise_double(buf, &psp->penalty_authfail)) != 0 ||
+           (r = deserialise_double(buf, &psp->penalty_invaliduser)) != 0 ||
+           (r = deserialise_double(buf, &psp->penalty_noauth)) != 0 ||
+           (r = deserialise_double(buf,
+           &psp->penalty_refuseconnection)) != 0 ||
+           (r = deserialise_double(buf, &psp->penalty_max)) != 0 ||
+           (r = deserialise_double(buf, &psp->penalty_min)) != 0) {
+               error_fr(r, "deserialise");
+               return r;
+       }
+
+       return 0;
+}
+
+static int
+deserialise_rekeylimit(ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = deserialise_s64(buf, &options->rekey_limit)) != 0 ||
+           (r = deserialise_s32(buf, &options->rekey_interval)) != 0) {
+               error_fr(r, "deserialise");
+               return r;
+       }
+
+       return 0;
+}
+
+static int
+deserialise_subsystem(ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+       u_int i, n;
+       char **names = NULL, **commands = NULL, **args = NULL;
+
+       if ((r = deserialise_count(buf, &n, "subsystems")) != 0)
+               return r;
+       if (n > 0) {
+               names = xcalloc(n, sizeof(*names));
+               commands = xcalloc(n, sizeof(*names));
+               args = xcalloc(n, sizeof(*args));
+       }
+       for (i = 0; i < n; i++) {
+               if ((r = sshbuf_get_cstring(buf, names + i, NULL)) != 0 ||
+                   (r = sshbuf_get_cstring(buf, commands + i, NULL)) != 0 ||
+                   (r = sshbuf_get_cstring(buf, args + i, NULL)) != 0) {
+                       error_fr(r, "deserialise member");
+                       free_string_array(names, i + 1);
+                       free_string_array(commands, i + 1);
+                       free_string_array(args, i + 1);
+                       return r;
+               }
+       }
+       options->num_subsystems = n;
+       options->subsystem_name = names;
+       options->subsystem_command = commands;
+       options->subsystem_args = args;
+       return 0;
+}
+
+static int
+deserialise_timingsecret(ServerOptions *options, struct sshbuf *buf)
+{
+       int r;
+
+       if ((r = sshbuf_get_u64(buf, &options->timing_secret)) != 0) {
+               error_fr(r, "deserialise");
+               return r;
+       }
+       return 0;
+}
+
+int
+deserialise_server_options(struct sshbuf *buf, ServerOptions *options)
+{
+       int r = SSH_ERR_INTERNAL_ERROR;
+       uint32_t version;
+       ServerOptions new_options;
+
+       initialize_server_options(&new_options);
+       if ((r = sshbuf_get_u32(buf, &version)) != 0) {
+               error_fr(r, "deserialise version");
+               goto out;
+       }
+       if (version != SSHD_CONFIG_BLOB_VERSION) {
+               error_f("unsupported config blob version %u", version);
+               r = SSH_ERR_INVALID_FORMAT;
+               goto out;
+       }
+#define SSHCONF_INT(var, conf, flags, ms, def, cp) \
+       if ((r = deserialise_s32(buf, &new_options.var)) != 0) { \
+               error_fr(r, "deserialise %s", #var); \
+               goto out; \
+       }
+#define SSHCONF_INTFLAG(var, conf, flags, def, cp) \
+       if ((r = deserialise_s32(buf, &new_options.var)) != 0) { \
+               error_fr(r, "deserialise %s", #var); \
+               goto out; \
+       }
+#define SSHCONF_STRING(var, conf, flags, cp) \
+       if ((r = deserialise_nullable_string(buf, &new_options.var)) != 0) { \
+               error_fr(r, "deserialise %s", #var); \
+               goto out; \
+       }
+#define SSHCONF_STRARRAY(var, nvar, conf, flags, cp) \
+       if ((r = deserialise_nullable_string_array(buf, &new_options.var, \
+           &new_options.nvar)) != 0) { \
+               error_fr(r, "deserialise %s", #var); \
+               goto out; \
+       }
+#define SSHCONF_CUSTOM(conf, funcsuffix, flags, cp) \
+       if ((r = deserialise_##funcsuffix(&new_options, buf)) != 0) \
+               goto out;
+#define SSHCONF_NONCONF(funcsuffix) \
+       if ((r = deserialise_##funcsuffix(&new_options, buf)) != 0) \
+               goto out;
+#define SSHCONF_NOSUPPORT(var, conf, opcode, flags)    /* empty */
+#define SSHCONF_ALIAS(old, conf, flags)                        /* empty */
+
+       SSHD_CONFIG_ENTRIES
+
+       if (sshbuf_len(buf) != 0) {
+               error_f("trailing data in config blob");
+               r = SSH_ERR_INVALID_FORMAT;
+               goto out;
+       }
+
+#undef SSHCONF_INT
+#undef SSHCONF_INTFLAG
+#undef SSHCONF_STRING
+#undef SSHCONF_STRARRAY
+#undef SSHCONF_CUSTOM
+#undef SSHCONF_NONCONF
+#undef SSHCONF_NOSUPPORT
+#undef SSHCONF_ALIAS
+
+       /* success */
+       r = 0;
+       free_server_options(options);
+       *options = new_options;
+       memset(&new_options, 0, sizeof(new_options));
+ out:
+       free_server_options(&new_options);
+       return r;
+}
+
+static void
+free_hostkeyfile(ServerOptions *options)
+{
+       u_int i;
+
+       for (i = 0; i < options->num_host_key_files; i++)
+               free(options->host_key_files[i]);
+       free(options->host_key_files);
+       free(options->host_key_file_userprovided);
+}
+
+static void
+free_listenaddress(ServerOptions *options)
+{
+       u_int i;
+
+       free_queued_listen_addrs(options->queued_listen_addrs,
+           options->num_queued_listens);
+
+       for (i = 0; i < options->num_listen_addrs; i++) {
+               free(options->listen_addrs[i].rdomain);
+               if (options->listen_addrs[i].addrs != NULL)
+                       freeaddrinfo(options->listen_addrs[i].addrs);
+       }
+       free(options->listen_addrs);
+}
+
+static void
+free_permituserenv(ServerOptions *options)
+{
+       free(options->permit_user_env_allowlist);
+}
+
+static void
+free_subsystem(ServerOptions *options)
+{
+       u_int i;
+
+       for (i = 0; i < options->num_subsystems; i++) {
+               free(options->subsystem_name[i]);
+               free(options->subsystem_command[i]);
+               free(options->subsystem_args[i]);
+       }
+       free(options->subsystem_name);
+       free(options->subsystem_command);
+       free(options->subsystem_args);
+}
+
+void
+free_server_options(ServerOptions *options)
+{
+#define SSHCONF_INT(var, conf, flags, ms, def, cp)     /* empty */
+#define SSHCONF_INTFLAG(var, conf, flags, def, cp)     /* empty */
+#define SSHCONF_STRING(var, conf, flags, cp)           free(options->var);
+#define SSHCONF_STRARRAY(var, nvar, conf, flags, cp) \
+       free_string_array(options->var, options->nvar);
+#define SSHCONF_CUSTOM(conf, funcsuffix, flags, cp) \
+       free_##funcsuffix(options);
+#define SSHCONF_NONCONF(funcsuffix) \
+       free_##funcsuffix(options);
+#define SSHCONF_NOSUPPORT(var, conf, opcode, flags)    /* empty */
+#define SSHCONF_ALIAS(old, conf, flags)                        /* empty */
+
+#define free_ipqos(options)
+#define free_logfacility(options)
+#define free_loglevel(options)
+#define free_port(options)
+#define free_gatewayports(options)
+#define free_streamlocalbindmask(options)
+#define free_streamlocalbindunlink(options)
+#define free_maxstartups(options)
+#define free_persourcenetblocksize(options)
+#define free_persourcepenalties(options)
+#define free_rekeylimit(options)
+#define free_timingsecret(options)
+
+       SSHD_CONFIG_ENTRIES
+
+#undef free_ipqos
+#undef free_logfacility
+#undef free_loglevel
+#undef free_port
+#undef free_gatewayports
+#undef free_streamlocalbindmask
+#undef free_streamlocalbindunlink
+#undef free_maxstartups
+#undef free_persourcenetblocksize
+#undef free_persourcepenalties
+#undef free_rekeylimit
+#undef free_timingsecret
+
+#undef SSHCONF_INT
+#undef SSHCONF_INTFLAG
+#undef SSHCONF_STRING
+#undef SSHCONF_STRARRAY
+#undef SSHCONF_CUSTOM
+#undef SSHCONF_NONCONF
+#undef SSHCONF_NOSUPPORT
+#undef SSHCONF_ALIAS
+
+       initialize_server_options(options);
+}
+
+static void
+copy_server_option_int(int *dst, int src)
+{
+       if (src != -1)
+               *dst = src;
+}
+
+static void
+copy_server_option_int64(int64_t *dst, int64_t src)
+{
+       if (src != -1)
+               *dst = src;
+}
+
+static void
+copy_server_option_string(char **dst, const char *src)
+{
+       if (src != NULL && *dst != src) {
+               free(*dst);
+               *dst = xstrdup(src);
+       }
+}
+
+static void
+copy_server_option_strarray_values(char ***dst, u_int ndst,
+    char * const *src, u_int nsrc)
+{
+       u_int i;
+
+       if (nsrc == 0)
+               return;
+       for (i = 0; i < ndst; i++)
+               free((*dst)[i]);
+       free(*dst);
+       *dst = xcalloc(nsrc, sizeof(**dst));
+       for (i = 0; i < nsrc; i++)
+               (*dst)[i] = src[i] == NULL ? NULL : xstrdup(src[i]);
+}
+
+static void
+copy_server_option_strarray(char ***dst, u_int *ndst,
+    char * const *src, u_int nsrc)
+{
+       if (nsrc == 0)
+               return;
+       copy_server_option_strarray_values(dst, *ndst, src, nsrc);
+       *ndst = nsrc;
+}
+
+static void
+copy_ipqos(ServerOptions *dst, const ServerOptions *src)
+{
+       copy_server_option_int(&dst->ip_qos_interactive,
+           src->ip_qos_interactive);
+       copy_server_option_int(&dst->ip_qos_bulk, src->ip_qos_bulk);
+}
+
+static void
+copy_gatewayports(ServerOptions *dst, const ServerOptions *src)
+{
+       copy_server_option_int(&dst->fwd_opts.gateway_ports,
+           src->fwd_opts.gateway_ports);
+}
+
+static void
+copy_streamlocalbindunlink(ServerOptions *dst, const ServerOptions *src)
+{
+       copy_server_option_int(&dst->fwd_opts.streamlocal_bind_unlink,
+           src->fwd_opts.streamlocal_bind_unlink);
+}
+
+static void
+copy_loglevel(ServerOptions *dst, const ServerOptions *src)
+{
+       if (src->log_level != -1)
+               dst->log_level = src->log_level;
+}
+
+static void
+copy_rekeylimit(ServerOptions *dst, const ServerOptions *src)
+{
+       copy_server_option_int64(&dst->rekey_limit, src->rekey_limit);
+       copy_server_option_int(&dst->rekey_interval, src->rekey_interval);
+}
+
+static void
+copy_subsystem(ServerOptions *dst, const ServerOptions *src)
+{
+       u_int old_num_subsystems = dst->num_subsystems;
+
+       if (src->num_subsystems == 0)
+               return;
+       copy_server_option_strarray_values(&dst->subsystem_name,
+           old_num_subsystems, src->subsystem_name, src->num_subsystems);
+       copy_server_option_strarray_values(&dst->subsystem_command,
+           old_num_subsystems, src->subsystem_command, src->num_subsystems);
+       copy_server_option_strarray_values(&dst->subsystem_args,
+           old_num_subsystems, src->subsystem_args, src->num_subsystems);
+       dst->num_subsystems = src->num_subsystems;
+}
+
+/*
+ * Copy any supported values that are set.
+ *
+ * If the preauth flag is set, skip the post-authentication-only manual
+ * string cleanup and subsystem merge below.
+ */
+void
+copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
+{
+       if (dst == src)
+               return;
+
+#define SSHCONF_INT(var, conf, flags, ms, def, cp) \
+       cp(copy_server_option_int(&dst->var, src->var);)
+#define SSHCONF_INTFLAG(var, conf, flags, def, cp) \
+       cp(copy_server_option_int(&dst->var, src->var);)
+#define SSHCONF_STRING(var, conf, flags, cp) \
+       cp(copy_server_option_string(&dst->var, src->var);)
+#define SSHCONF_STRARRAY(var, nvar, conf, flags, cp) \
+       cp(copy_server_option_strarray(&dst->var, &dst->nvar, \
+           src->var, src->nvar);)
+#define SSHCONF_CUSTOM(conf, funcsuffix, flags, cp) \
+       cp(copy_##funcsuffix(dst, src);)
+#define SSHCONF_NONCONF(funcsuffix)                    /* empty */
+#define SSHCONF_NOSUPPORT(var, conf, opcode, flags)    /* empty */
+#define SSHCONF_ALIAS(old, conf, flags)                        /* empty */
+
+       SSHD_CONFIG_ENTRIES
+
+#undef SSHCONF_INT
+#undef SSHCONF_INTFLAG
+#undef SSHCONF_STRING
+#undef SSHCONF_STRARRAY
+#undef SSHCONF_CUSTOM
+#undef SSHCONF_NONCONF
+#undef SSHCONF_NOSUPPORT
+#undef SSHCONF_ALIAS
+
+       /*
+        * The bind_mask is a mode_t that may be unsigned, so we can't use
+        * copy_server_option_int - it does a signed comparison that causes
+        * compiler warnings.
+        */
+       if (src->fwd_opts.streamlocal_bind_mask != (mode_t)-1) {
+               dst->fwd_opts.streamlocal_bind_mask =
+                   src->fwd_opts.streamlocal_bind_mask;
+       }
+
+       /* Arguments that accept '+...' need to be expanded */
+       assemble_algorithms(dst);
+
+       /*
+        * The only things that should be below this point are string options
+        * which are only used after authentication.
+        */
+       if (preauth)
+               return;
+
+       /* These options may be "none" to clear a global setting */
+       copy_server_option_string(&dst->adm_forced_command,
+           src->adm_forced_command);
+       if (option_clear_or_none(dst->adm_forced_command)) {
+               free(dst->adm_forced_command);
+               dst->adm_forced_command = NULL;
+       }
+       copy_server_option_string(&dst->chroot_directory,
+           src->chroot_directory);
+       if (option_clear_or_none(dst->chroot_directory)) {
+               free(dst->chroot_directory);
+               dst->chroot_directory = NULL;
+       }
+
+       /* Subsystems require merging. */
+       servconf_merge_subsystems(dst, src);
+}
 
 #define SERVCONF_MAX_DEPTH     16
 static void
@@ -3306,7 +4178,7 @@ dump_config(ServerOptions *o)
        dump_cfg_fmtint(sPermitUserRC, o->permit_user_rc);
        dump_cfg_fmtint(sStrictModes, o->strict_modes);
        dump_cfg_fmtint(sTCPKeepAlive, o->tcp_keep_alive);
-       dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd);
+       dump_cfg_fmtint(sPermitEmptyPasswords, o->permit_empty_passwd);
        dump_cfg_fmtint(sCompression, o->compression);
        dump_cfg_fmtint(sGatewayPorts, o->fwd_opts.gateway_ports);
        dump_cfg_fmtint(sUseDNS, o->use_dns);
@@ -3353,15 +4225,14 @@ dump_config(ServerOptions *o)
 
        /* string arguments requiring a lookup */
        dump_cfg_string(sLogLevel, log_level_name(o->log_level));
-       dump_cfg_string(sLogFacility, log_facility_name(o->log_facility));
+       dump_cfg_string(sSyslogFacility, log_facility_name(o->log_facility));
 
        /* string array arguments */
        dump_cfg_strarray_oneline(sAuthorizedKeysFile, o->num_authkeys_files,
            o->authorized_keys_files);
        dump_cfg_strarray_oneline(sRevokedKeys, o->num_revoked_keys_files,
            o->revoked_keys_files);
-       dump_cfg_strarray(sHostKeyFile, o->num_host_key_files,
-           o->host_key_files);
+       dump_cfg_strarray(sHostKey, o->num_host_key_files, o->host_key_files);
        dump_cfg_strarray(sHostCertificate, o->num_host_cert_files,
            o->host_cert_files);
        dump_cfg_strarray(sAllowUsers, o->num_allow_users, o->allow_users);
index b06db09e109043907c3d7ec4615b004ca681a407..a78450343eb386f960e08a2cb9664ee2b1889387 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.h,v 1.176 2026/03/03 09:57:25 dtucker Exp $ */
+/* $OpenBSD: servconf.h,v 1.177 2026/05/31 11:30:50 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -18,6 +18,8 @@
 
 #include <sys/queue.h>
 
+struct sshbuf;
+
 #define MAX_PORTS              256     /* Max # ports. */
 
 /* permit_root_login */
 #define PUBKEYAUTH_TOUCH_REQUIRED      (1)
 #define PUBKEYAUTH_VERIFY_REQUIRED     (1<<1)
 
+/* Various defaults */
+#define SSHD_DEFAULT_LOGIN_GRACE_TIME  120
+#define SSHD_DEFAULT_X11_DISPLAY_OFFSET        10
+#ifdef WITH_ZLIB
+#define SSHD_DEFAULT_COMPRESSION       COMP_DELAYED
+#else
+#define SSHD_DEFAULT_COMPRESSION       COMP_NONE
+#endif
+
 struct ssh;
 
 /*
@@ -83,179 +94,306 @@ struct per_source_penalty {
        double  penalty_min;
 };
 
-typedef struct {
+/* Options for whether config entries are copied after Match processing */
+#define SSHCFG_COPY_NONE(action)
+#define SSHCFG_COPY_MATCH(action)      action
+#define SSHCFG_COPY_MANUAL(action)
+
+/*
+ * This macro is used to generate most of ServerOptions and some of the
+ * parsing and de/serialisation code in servconf.c. Every variable in
+ * ServerOptions *must* be represented here.
+ *
+ * Variables and configuration options that need special handling (e.g.
+ * those that represent a struct or use a single option to populate multiple
+ * values) use the SSHCONF_CUSTOM macro and get manual variable entries in
+ * ServerOptions below.
+ *
+ * Variables that exist in ServerOptions but aren't populated by a keyword
+ * use the SSHCONF_NONCONF macro and also get manual entries in ServerOptions.
+ *
+ * Everything else uses one of the SSHCONF_INT, SSHCONF_INTFLAG,
+ * SSHCONF_STRING, or SSHCONF_STRARRAY macros. These automatically populate
+ * their corresponding variable definitions in ServerOptions. The integer
+ * options also include defaults for initialisation.
+ *
+ * Unsupported, deprecated and ignored options use SSHCONF_NOSUPPORT and
+ * don't populate ServerOptions. Deprecated aliases that still work use
+ * SSHCONF_ALIAS.
+ *
+ * Why go to all this trouble? It ensures a level of consistency between
+ * the configuration structure and the parsing code and helps us write
+ * serialisation/deserialisation functions that we can be pretty sure will
+ * capture every value in the configuration file.
+ *
+ * Entry formats:
+ *   SSHCONF_INT(field, keyword, scope, multistate, default, copy)
+ *   SSHCONF_INTFLAG(field, keyword, scope, default, copy)
+ *   SSHCONF_STRING(field, keyword, scope, copy)
+ *   SSHCONF_STRARRAY(field, nfield, keyword, scope, copy)
+ *   SSHCONF_CUSTOM(keyword, suffix, scope, copy)
+ *   SSHCONF_NONCONF(suffix)
+ *   SSHCONF_NOSUPPORT(field, keyword, token, scope)
+ *   SSHCONF_ALIAS(old_keyword, keyword, scope)
+ */
+#define SSHD_CONFIG_ENTRIES_CUSTOM \
+SSHCONF_CUSTOM(Port, port, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_CUSTOM(ListenAddress, listenaddress, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_CUSTOM(HostKey, hostkeyfile, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_CUSTOM(IPQoS, ipqos, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_CUSTOM(GatewayPorts, gatewayports, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_CUSTOM(StreamLocalBindMask, streamlocalbindmask, SSHCFG_ALL, SSHCFG_COPY_MANUAL) \
+SSHCONF_CUSTOM(StreamLocalBindUnlink, streamlocalbindunlink, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_CUSTOM(SyslogFacility, logfacility, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_CUSTOM(LogLevel, loglevel, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_CUSTOM(PermitUserEnvironment, permituserenv, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_CUSTOM(Subsystem, subsystem, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_CUSTOM(MaxStartups, maxstartups, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_CUSTOM(PerSourceNetBlockSize, persourcenetblocksize, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_CUSTOM(PerSourcePenalties, persourcepenalties, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_CUSTOM(RekeyLimit, rekeylimit, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_NONCONF(timingsecret)
+
+#define SSHD_CONFIG_ENTRIES_MAIN \
+SSHCONF_INT(address_family, AddressFamily, SSHCFG_GLOBAL, multistate_addressfamily, AF_UNSPEC, SSHCFG_COPY_NONE) \
+SSHCONF_STRING(routing_domain, RDomain, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRARRAY(host_cert_files, num_host_cert_files, HostCertificate, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_STRING(host_key_agent, HostKeyAgent, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_STRING(pid_file, PidFile, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_STRING(moduli_file, ModuliFile, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_INT(login_grace_time, LoginGraceTime, SSHCFG_GLOBAL, NULL, SSHD_DEFAULT_LOGIN_GRACE_TIME, SSHCFG_COPY_NONE) \
+SSHCONF_INT(permit_root_login, PermitRootLogin, SSHCFG_ALL, multistate_permitrootlogin, PERMIT_NO_PASSWD, SSHCFG_COPY_MATCH) \
+SSHCONF_INT(ignore_rhosts, IgnoreRhosts, SSHCFG_ALL, multistate_ignore_rhosts, 1, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(ignore_user_known_hosts, IgnoreUserKnownHosts, SSHCFG_GLOBAL, 0, SSHCFG_COPY_NONE) \
+SSHCONF_INTFLAG(print_motd, PrintMotd, SSHCFG_GLOBAL, 1, SSHCFG_COPY_NONE) \
+SSHCONF_INTFLAG(x11_forwarding, X11Forwarding, SSHCFG_ALL, 0, SSHCFG_COPY_MATCH) \
+SSHCONF_INT(x11_display_offset, X11DisplayOffset, SSHCFG_ALL, NULL, SSHD_DEFAULT_X11_DISPLAY_OFFSET, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(x11_use_localhost, X11UseLocalhost, SSHCFG_ALL, 1, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(xauth_location, XAuthLocation, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_INTFLAG(permit_tty, PermitTTY, SSHCFG_ALL, 1, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(permit_user_rc, PermitUserRC, SSHCFG_ALL, 1, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(strict_modes, StrictModes, SSHCFG_GLOBAL, 1, SSHCFG_COPY_NONE) \
+SSHCONF_INTFLAG(tcp_keep_alive, TCPKeepAlive, SSHCFG_GLOBAL, 1, SSHCFG_COPY_NONE) \
+SSHCONF_STRING(ciphers, Ciphers, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_STRING(macs, Macs, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_STRING(kex_algorithms, KexAlgorithms, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_STRARRAY(log_verbose, num_log_verbose, LogVerbose, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(hostbased_authentication, HostbasedAuthentication, SSHCFG_ALL, 0, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(hostbased_uses_name_from_packet_only, HostbasedUsesNameFromPacketOnly, SSHCFG_ALL, 0, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(hostbased_accepted_algos, HostbasedAcceptedAlgorithms, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(hostkeyalgorithms, HostKeyAlgorithms, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_STRING(ca_sign_algorithms, CASignatureAlgorithms, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_INT(pubkey_auth_options, PubkeyAuthOptions, SSHCFG_ALL, NULL, 0, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(pubkey_authentication, PubkeyAuthentication, SSHCFG_ALL, 1, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(pubkey_accepted_algos, PubkeyAcceptedAlgorithms, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(password_authentication, PasswordAuthentication, SSHCFG_ALL, 1, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(kbd_interactive_authentication, KbdInteractiveAuthentication, SSHCFG_ALL, 1, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(permit_empty_passwd, PermitEmptyPasswords, SSHCFG_ALL, 0, SSHCFG_COPY_MATCH) \
+SSHCONF_INT(compression, Compression, SSHCFG_GLOBAL, multistate_compression, SSHD_DEFAULT_COMPRESSION, SSHCFG_COPY_NONE) \
+SSHCONF_INT(allow_tcp_forwarding, AllowTcpForwarding, SSHCFG_ALL, multistate_tcpfwd, FORWARD_ALLOW, SSHCFG_COPY_MATCH) \
+SSHCONF_INT(allow_streamlocal_forwarding, AllowStreamLocalForwarding, SSHCFG_ALL, multistate_tcpfwd, FORWARD_ALLOW, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(allow_agent_forwarding, AllowAgentForwarding, SSHCFG_ALL, 1, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(disable_forwarding, DisableForwarding, SSHCFG_ALL, 0, SSHCFG_COPY_MATCH) \
+SSHCONF_STRARRAY(allow_users, num_allow_users, AllowUsers, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRARRAY(deny_users, num_deny_users, DenyUsers, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRARRAY(allow_groups, num_allow_groups, AllowGroups, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRARRAY(deny_groups, num_deny_groups, DenyGroups, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRARRAY(accept_env, num_accept_env, AcceptEnv, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRARRAY(setenv, num_setenv, SetEnv, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_INT(per_source_max_startups, PerSourceMaxStartups, SSHCFG_GLOBAL, NULL, INT_MAX, SSHCFG_COPY_NONE) \
+SSHCONF_STRING(per_source_penalty_exempt, PerSourcePenaltyExemptList, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_INT(max_authtries, MaxAuthTries, SSHCFG_ALL, NULL, DEFAULT_AUTH_FAIL_MAX, SSHCFG_COPY_MATCH) \
+SSHCONF_INT(max_sessions, MaxSessions, SSHCFG_ALL, NULL, DEFAULT_SESSIONS_MAX, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(banner, Banner, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(use_dns, UseDNS, SSHCFG_GLOBAL, 0, SSHCFG_COPY_NONE) \
+SSHCONF_INT(client_alive_interval, ClientAliveInterval, SSHCFG_ALL, NULL, 0, SSHCFG_COPY_MATCH) \
+SSHCONF_INT(client_alive_count_max, ClientAliveCountMax, SSHCFG_ALL, NULL, 3, SSHCFG_COPY_MATCH) \
+SSHCONF_STRARRAY(authorized_keys_files, num_authkeys_files, AuthorizedKeysFile, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(adm_forced_command, ForceCommand, SSHCFG_ALL, SSHCFG_COPY_MANUAL) \
+SSHCONF_INTFLAG(permit_tun, PermitTunnel, SSHCFG_ALL, SSH_TUNMODE_NO, SSHCFG_COPY_MATCH) \
+SSHCONF_STRARRAY(permitted_opens, num_permitted_opens, PermitOpen, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRARRAY(permitted_listens, num_permitted_listens, PermitListen, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(chroot_directory, ChrootDirectory, SSHCFG_ALL, SSHCFG_COPY_MANUAL) \
+SSHCONF_STRARRAY(revoked_keys_files, num_revoked_keys_files, RevokedKeys, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(trusted_user_ca_keys, TrustedUserCAKeys, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(authorized_keys_command, AuthorizedKeysCommand, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(authorized_keys_command_user, AuthorizedKeysCommandUser, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(authorized_principals_file, AuthorizedPrincipalsFile, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(authorized_principals_command, AuthorizedPrincipalsCommand, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(authorized_principals_command_user, AuthorizedPrincipalsCommandUser, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(version_addendum, VersionAddendum, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_STRARRAY(auth_methods, num_auth_methods, AuthenticationMethods, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_INT(fingerprint_hash, FingerprintHash, SSHCFG_GLOBAL, NULL, SSH_FP_HASH_DEFAULT, SSHCFG_COPY_NONE) \
+SSHCONF_INTFLAG(expose_userauth_info, ExposeAuthInfo, SSHCFG_ALL, 0, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(sk_provider, SecurityKeyProvider, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_INT(required_rsa_size, RequiredRSASize, SSHCFG_ALL, NULL, SSH_RSA_MINIMUM_MODULUS_SIZE, SSHCFG_COPY_MATCH) \
+SSHCONF_STRARRAY(channel_timeouts, num_channel_timeouts, ChannelTimeout, SSHCFG_ALL, SSHCFG_COPY_MATCH) \
+SSHCONF_INT(unused_connection_timeout, UnusedConnectionTimeout, SSHCFG_ALL, NULL, 0, SSHCFG_COPY_MATCH) \
+SSHCONF_STRING(sshd_session_path, SshdSessionPath, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_STRING(sshd_auth_path, SshdAuthPath, SSHCFG_GLOBAL, SSHCFG_COPY_NONE) \
+SSHCONF_INTFLAG(refuse_connection, RefuseConnection, SSHCFG_ALL, 0, SSHCFG_COPY_MATCH)
+
+#define SSHD_CONFIG_ENTRIES_LEGACY \
+SSHCONF_NOSUPPORT(server_key_bits, ServerKeyBits, SSHCONF_DEPRECATED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(key_regeneration_interval, KeyRegenerationInterval, SSHCONF_DEPRECATED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(rhosts_authentication, RHostsAuthentication, SSHCONF_DEPRECATED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(rhosts_rsa_authentication, RhostsRSAAuthentication, SSHCONF_DEPRECATED, SSHCFG_ALL) \
+SSHCONF_NOSUPPORT(rsa_authentication, RSAAuthentication, SSHCONF_DEPRECATED, SSHCFG_ALL) \
+SSHCONF_NOSUPPORT(check_mail, CheckMail, SSHCONF_DEPRECATED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(use_login, UseLogin, SSHCONF_DEPRECATED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(verify_reverse_mapping, VerifyReverseMapping, SSHCONF_DEPRECATED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(reverse_mapping_check, ReverseMappingCheck, SSHCONF_DEPRECATED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(authorized_keys_file2, AuthorizedKeysFile2, SSHCONF_DEPRECATED, SSHCFG_ALL) \
+SSHCONF_NOSUPPORT(use_privilege_separation, UsePrivilegeSeparation, SSHCONF_DEPRECATED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(protocol, Protocol, SSHCONF_IGNORE, SSHCFG_GLOBAL)
+
+#define SSHD_CONFIG_ENTRIES_ALIASES \
+SSHCONF_ALIAS(HostDSAKey, HostKey, SSHCFG_GLOBAL) \
+SSHCONF_ALIAS(HostBasedAcceptedKeyTypes, HostbasedAcceptedAlgorithms, SSHCFG_ALL) \
+SSHCONF_ALIAS(PubkeyAcceptedKeyTypes, PubkeyAcceptedAlgorithms, SSHCFG_ALL) \
+SSHCONF_ALIAS(DSAAuthentication, PubkeyAuthentication, SSHCFG_GLOBAL) \
+SSHCONF_ALIAS(ChallengeResponseAuthentication, KbdInteractiveAuthentication, SSHCFG_ALL) \
+SSHCONF_ALIAS(SKeyAuthentication, KbdInteractiveAuthentication, SSHCFG_ALL) \
+SSHCONF_ALIAS(KeepAlive, TCPKeepAlive, SSHCFG_GLOBAL)
+
+#define SSHD_CONFIG_ENTRIES_BASE \
+       SSHD_CONFIG_ENTRIES_CUSTOM \
+       SSHD_CONFIG_ENTRIES_MAIN \
+       SSHD_CONFIG_ENTRIES_LEGACY \
+       SSHD_CONFIG_ENTRIES_ALIASES \
+       SSHD_CONFIG_ENTRIES_LASTLOG
+
+#ifdef DISABLE_LASTLOG
+#define SSHD_CONFIG_ENTRIES_LASTLOG \
+SSHCONF_NOSUPPORT(print_lastlog, PrintLastLog, SSHCONF_UNSUPPORTED, SSHCFG_GLOBAL)
+#else
+#define SSHD_CONFIG_ENTRIES_LASTLOG \
+SSHCONF_INTFLAG(print_lastlog, PrintLastLog, SSHCFG_GLOBAL, 1, SSHCFG_COPY_NONE)
+#endif
+
+/* Compile-time enabled options */
+#ifdef KRB5
+
+#ifdef USE_AFS
+#define SSHD_CONFIG_KRB5_AFS \
+SSHCONF_INTFLAG(kerberos_get_afs_token, KerberosGetAFSToken, SSHCFG_GLOBAL, 0, SSHCFG_COPY_NONE)
+#else /* USE_AFS */
+#define SSHD_CONFIG_KRB5_AFS \
+SSHCONF_NOSUPPORT(kerberos_get_afs_token, KerberosGetAFSToken, SSHCONF_UNSUPPORTED, SSHCFG_GLOBAL)
+#endif /* USE_AFS */
+
+#define SSHD_CONFIG_ENTRIES_KRB5 \
+SSHCONF_INTFLAG(kerberos_authentication, KerberosAuthentication, SSHCFG_ALL, 0, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(kerberos_or_local_passwd, KerberosOrLocalPasswd, SSHCFG_GLOBAL, 1, SSHCFG_COPY_NONE) \
+SSHCONF_INTFLAG(kerberos_ticket_cleanup, KerberosTicketCleanup, SSHCFG_GLOBAL, 1, SSHCFG_COPY_NONE) \
+SSHD_CONFIG_KRB5_AFS
+#else /* KRB5 */
+#define SSHD_CONFIG_ENTRIES_KRB5 \
+SSHCONF_NOSUPPORT(kerberos_authentication, KerberosAuthentication, SSHCONF_UNSUPPORTED, SSHCFG_ALL) \
+SSHCONF_NOSUPPORT(kerberos_or_local_passwd, KerberosOrLocalPasswd, SSHCONF_UNSUPPORTED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(kerberos_ticket_cleanup, KerberosTicketCleanup, SSHCONF_UNSUPPORTED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(kerberos_get_afs_token, KerberosGetAFSToken, SSHCONF_UNSUPPORTED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(kerberos_tgt_passing, KerberosTgtPassing, SSHCONF_UNSUPPORTED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(afs_token_passing, AFSTokenPassing, SSHCONF_UNSUPPORTED, SSHCFG_GLOBAL)
+#endif /* KRB5 */
+
+#ifdef GSSAPI
+#define SSHD_CONFIG_ENTRIES_GSS \
+SSHCONF_INTFLAG(gss_authentication, GssAuthentication, SSHCFG_ALL, 0, SSHCFG_COPY_MATCH) \
+SSHCONF_INTFLAG(gss_cleanup_creds, GssCleanupCreds, SSHCFG_GLOBAL, 1, SSHCFG_COPY_NONE) \
+SSHCONF_INTFLAG(gss_deleg_creds, GssDelegateCreds, SSHCFG_GLOBAL, 1, SSHCFG_COPY_NONE) \
+SSHCONF_INTFLAG(gss_strict_acceptor, GssStrictAcceptor, SSHCFG_GLOBAL, 1, SSHCFG_COPY_NONE)
+#else /* GSSAPI */
+#define SSHD_CONFIG_ENTRIES_GSS \
+SSHCONF_NOSUPPORT(gss_authentication, GssAuthentication, SSHCONF_UNSUPPORTED, SSHCFG_ALL) \
+SSHCONF_NOSUPPORT(gss_cleanup_creds, GssCleanupCreds, SSHCONF_UNSUPPORTED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(gss_deleg_creds, GssDelegateCreds, SSHCONF_UNSUPPORTED, SSHCFG_GLOBAL) \
+SSHCONF_NOSUPPORT(gss_strict_acceptor, GssStrictAcceptor, SSHCONF_UNSUPPORTED, SSHCFG_GLOBAL)
+#endif /* GSSAPI */
+
+#define SSHD_CONFIG_ENTRIES \
+       SSHD_CONFIG_ENTRIES_BASE \
+       SSHD_CONFIG_ENTRIES_KRB5 \
+       SSHD_CONFIG_ENTRIES_GSS \
+
+/* Macros to declare ServerOptions member variables */
+#define SSHCONF_INT(var, conf, flags, ms, def, cp)     int var;
+#define SSHCONF_INTFLAG(var, conf, flags, def, cp)     int var;
+#define SSHCONF_STRING(var, conf, flags, cp)           char *var;
+#define SSHCONF_STRARRAY(var, nvar, conf, flags, cp)   \
+       char **var; \
+       u_int nvar;
+#define SSHCONF_CUSTOM(conf, funcsuffix, flags, cp)    /* empty */
+#define SSHCONF_NONCONF(funcsuffix)                    /* empty */
+#define SSHCONF_NOSUPPORT(var, conf, opcode, flags)    /* empty */
+#define SSHCONF_ALIAS(old, conf, flags)                        /* empty */
+
+typedef struct ServerOptions {
+       SSHD_CONFIG_ENTRIES
+       /* Ports */
        u_int   num_ports;
        u_int   ports_from_cmdline;
        int     ports[MAX_PORTS];       /* Port number to listen on. */
+       /* ListenAddress */
        struct queued_listenaddr *queued_listen_addrs;
        u_int   num_queued_listens;
        struct listenaddr *listen_addrs;
        u_int   num_listen_addrs;
-       int     address_family;         /* Address family used by the server. */
-
-       char    *routing_domain;        /* Bind session to routing domain */
-
+       /* HostKey */
        char   **host_key_files;        /* Files containing host keys. */
        int     *host_key_file_userprovided; /* Key was specified by user. */
        u_int   num_host_key_files;     /* Number of files for host keys. */
-       char   **host_cert_files;       /* Files containing host certs. */
-       u_int   num_host_cert_files;    /* Number of files for host certs. */
-
-       char   *host_key_agent;         /* ssh-agent socket for host keys. */
-       char   *pid_file;               /* Where to put our pid */
-       char   *moduli_file;            /* moduli file for DH-GEX */
-       int     login_grace_time;       /* Disconnect if no auth in this time
-                                        * (sec). */
-       int     permit_root_login;      /* PERMIT_*, see above */
-       int     ignore_rhosts;  /* Ignore .rhosts and .shosts. */
-       int     ignore_user_known_hosts;        /* Ignore ~/.ssh/known_hosts
-                                                * for RhostsRsaAuth */
-       int     print_motd;     /* If true, print /etc/motd. */
-       int     print_lastlog;  /* If true, print lastlog */
-       int     x11_forwarding; /* If true, permit inet (spoofing) X11 fwd. */
-       int     x11_display_offset;     /* What DISPLAY number to start
-                                        * searching at */
-       int     x11_use_localhost;      /* If true, use localhost for fake X11 server. */
-       char   *xauth_location; /* Location of xauth program */
-       int     permit_tty;     /* If false, deny pty allocation */
-       int     permit_user_rc; /* If false, deny ~/.ssh/rc execution */
-       int     strict_modes;   /* If true, require string home dir modes. */
-       int     tcp_keep_alive; /* If true, set SO_KEEPALIVE. */
+       /* IPQoS */
        int     ip_qos_interactive;     /* IP ToS/DSCP/class for interactive */
        int     ip_qos_bulk;            /* IP ToS/DSCP/class for bulk traffic */
-       char   *ciphers;        /* Supported SSH2 ciphers. */
-       char   *macs;           /* Supported SSH2 macs. */
-       char   *kex_algorithms; /* SSH2 kex methods in order of preference. */
+       /* GatewayPorts, StreamLocalBindMask, StreamLocalBindUnlink */
        struct ForwardOptions fwd_opts; /* forwarding options */
+       /* LogFacility */
        SyslogFacility log_facility;    /* Facility for system logging. */
+       /* LogLevel */
        LogLevel log_level;     /* Level for system logging. */
-       u_int   num_log_verbose;        /* Verbose log overrides */
-       char    **log_verbose;
-       int     hostbased_authentication;       /* If true, permit ssh2 hostbased auth */
-       int     hostbased_uses_name_from_packet_only; /* experimental */
-       char   *hostbased_accepted_algos; /* Algos allowed for hostbased */
-       char   *hostkeyalgorithms;      /* SSH2 server key types */
-       char   *ca_sign_algorithms;     /* Allowed CA signature algorithms */
-       int     pubkey_authentication;  /* If true, permit ssh2 pubkey authentication. */
-       char   *pubkey_accepted_algos;  /* Signature algos allowed for pubkey */
-       int     pubkey_auth_options;    /* -1 or mask of PUBKEYAUTH_* flags */
-       int     kerberos_authentication;        /* If true, permit Kerberos
-                                                * authentication. */
-       int     kerberos_or_local_passwd;       /* If true, permit kerberos
-                                                * and any other password
-                                                * authentication mechanism,
-                                                * such as SecurID or
-                                                * /etc/passwd */
-       int     kerberos_ticket_cleanup;        /* If true, destroy ticket
-                                                * file on logout. */
-       int     kerberos_get_afs_token;         /* If true, try to get AFS token if
-                                                * authenticated with Kerberos. */
-       int     gss_authentication;     /* If true, permit GSSAPI authentication */
-       int     gss_cleanup_creds;      /* If true, destroy cred cache on logout */
-       int     gss_deleg_creds;        /* If true, accept delegated GSS credentials */
-       int     gss_strict_acceptor;    /* If true, restrict the GSSAPI acceptor name */
-       int     password_authentication;        /* If true, permit password
-                                                * authentication. */
-       int     kbd_interactive_authentication; /* If true, permit */
-       int     permit_empty_passwd;    /* If false, do not permit empty
-                                        * passwords. */
+       /* PermitUserEnvironment */
        int     permit_user_env;        /* If true, read ~/.ssh/environment */
        char   *permit_user_env_allowlist; /* pattern-list of allowed env names */
-       int     compression;    /* If true, compression is allowed */
-       int     allow_tcp_forwarding; /* One of FORWARD_* */
-       int     allow_streamlocal_forwarding; /* One of FORWARD_* */
-       int     allow_agent_forwarding;
-       int     disable_forwarding;
-       u_int num_allow_users;
-       char   **allow_users;
-       u_int num_deny_users;
-       char   **deny_users;
-       u_int num_allow_groups;
-       char   **allow_groups;
-       u_int num_deny_groups;
-       char   **deny_groups;
-
+       /* Subsystem */
        u_int num_subsystems;
        char   **subsystem_name;
        char   **subsystem_command;
        char   **subsystem_args;
-
-       u_int num_accept_env;
-       char   **accept_env;
-       u_int num_setenv;
-       char   **setenv;
-
+       /* MaxStartups */
        int     max_startups_begin;
        int     max_startups_rate;
        int     max_startups;
-       int     per_source_max_startups;
+       /* PerSourceNetBlockSize */
        int     per_source_masklen_ipv4;
        int     per_source_masklen_ipv6;
-       char    *per_source_penalty_exempt;
+       /* PerSourcePenalties */
        struct per_source_penalty per_source_penalty;
-       int     max_authtries;
-       int     max_sessions;
-       char   *banner;                 /* SSH-2 banner message */
-       int     use_dns;
-       int     client_alive_interval;  /*
-                                        * poke the client this often to
-                                        * see if it's still there
-                                        */
-       int     client_alive_count_max; /*
-                                        * If the client is unresponsive
-                                        * for this many intervals above,
-                                        * disconnect the session
-                                        */
-
-       u_int   num_authkeys_files;     /* Files containing public keys */
-       char   **authorized_keys_files;
-
-       char   *adm_forced_command;
-
-       int     use_pam;                /* Enable auth via PAM */
-       char   *pam_service_name;
-
-       int     permit_tun;
-
-       char   **permitted_opens;       /* May also be one of PERMITOPEN_* */
-       u_int   num_permitted_opens;
-       char   **permitted_listens; /* May also be one of PERMITOPEN_* */
-       u_int   num_permitted_listens;
-
-       char   *chroot_directory;
-       uint    num_revoked_keys_files;
-       char   **revoked_keys_files;
-       char   *trusted_user_ca_keys;
-       char   *authorized_keys_command;
-       char   *authorized_keys_command_user;
-       char   *authorized_principals_file;
-       char   *authorized_principals_command;
-       char   *authorized_principals_command_user;
-
+       /* RekeyLimit */
        int64_t rekey_limit;
        int     rekey_interval;
-
-       char   *version_addendum;       /* Appended to SSH banner */
-
-       u_int   num_auth_methods;
-       char   **auth_methods;
-
-       int     fingerprint_hash;
-       int     expose_userauth_info;
+       /* Passed by config but not keyword for this */
        uint64_t timing_secret;
-       char   *sk_provider;
-       int     required_rsa_size;      /* minimum size of RSA keys */
-
-       char    **channel_timeouts;     /* inactivity timeout by channel type */
-       u_int   num_channel_timeouts;
-
-       int     unused_connection_timeout;
-
-       char   *sshd_session_path;
-       char   *sshd_auth_path;
-
-       int     refuse_connection;
+       /* Placeholders for compile-time disabled things */
+       /* XXX djm redo macros to remove these */
+#ifndef WITH_PAM
+       int use_pam;
+#endif
+#ifdef DISABLE_LASTLOG
+       int print_lastlog;
+#endif
+#ifndef KRB5
+       int kerberos_authentication;
+#endif
 }       ServerOptions;
+#undef SSHCONF_INT
+#undef SSHCONF_INTFLAG
+#undef SSHCONF_STRING
+#undef SSHCONF_STRARRAY
+#undef SSHCONF_CUSTOM
+#undef SSHCONF_NONCONF
+#undef SSHCONF_NOSUPPORT
+#undef SSHCONF_ALIAS
 
 /* Information about the incoming connection as used by Match */
 struct connection_info {
@@ -280,49 +418,6 @@ struct include_item {
 TAILQ_HEAD(include_list, include_item);
 
 
-/*
- * These are string config options that must be copied between the
- * Match sub-config and the main config, and must be sent from the
- * privsep child to the privsep master. We use a macro to ensure all
- * the options are copied and the copies are done in the correct order.
- *
- * NB. an option must appear in servconf.c:copy_set_server_options() or
- * COPY_MATCH_STRING_OPTS here but never both.
- */
-#define COPY_MATCH_STRING_OPTS() do { \
-               M_CP_STROPT(banner); \
-               M_CP_STROPT(trusted_user_ca_keys); \
-               M_CP_STROPT(authorized_keys_command); \
-               M_CP_STROPT(authorized_keys_command_user); \
-               M_CP_STROPT(authorized_principals_file); \
-               M_CP_STROPT(authorized_principals_command); \
-               M_CP_STROPT(authorized_principals_command_user); \
-               M_CP_STROPT(hostbased_accepted_algos); \
-               M_CP_STROPT(pubkey_accepted_algos); \
-               M_CP_STROPT(ca_sign_algorithms); \
-               M_CP_STROPT(routing_domain); \
-               M_CP_STROPT(permit_user_env_allowlist); \
-               M_CP_STROPT(pam_service_name); \
-               M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files, 1);\
-               M_CP_STRARRAYOPT(revoked_keys_files, \
-                   num_revoked_keys_files, 1); \
-               M_CP_STRARRAYOPT(allow_users, num_allow_users, 1); \
-               M_CP_STRARRAYOPT(deny_users, num_deny_users, 1); \
-               M_CP_STRARRAYOPT(allow_groups, num_allow_groups, 1); \
-               M_CP_STRARRAYOPT(deny_groups, num_deny_groups, 1); \
-               M_CP_STRARRAYOPT(accept_env, num_accept_env, 1); \
-               M_CP_STRARRAYOPT(setenv, num_setenv, 1); \
-               M_CP_STRARRAYOPT(auth_methods, num_auth_methods, 1); \
-               M_CP_STRARRAYOPT(permitted_opens, num_permitted_opens, 1); \
-               M_CP_STRARRAYOPT(permitted_listens, num_permitted_listens, 1); \
-               M_CP_STRARRAYOPT(channel_timeouts, num_channel_timeouts, 1); \
-               M_CP_STRARRAYOPT(log_verbose, num_log_verbose, 1); \
-               /* Note: don't clobber num_subsystems until all copied */ \
-               M_CP_STRARRAYOPT(subsystem_name, num_subsystems, 0); \
-               M_CP_STRARRAYOPT(subsystem_command, num_subsystems, 0); \
-               M_CP_STRARRAYOPT(subsystem_args, num_subsystems, 1); \
-       } while (0)
-
 void    initialize_server_options(ServerOptions *);
 void    fill_default_server_options(ServerOptions *);
 int     process_server_config_line(ServerOptions *, char *, const char *, int,
@@ -342,4 +437,8 @@ void         servconf_add_hostkey(const char *, const int,
 void    servconf_add_hostcert(const char *, const int,
            ServerOptions *, const char *path);
 
+int     serialise_server_options(const ServerOptions *, struct sshbuf **);
+int     deserialise_server_options(struct sshbuf *, ServerOptions *);
+void    free_server_options(ServerOptions *);
+
 #endif                         /* SERVCONF_H */
index 76350a2a3501ab543e3009a90dc1e0234bb96de1..ad02aa48cdd48bea512b75bd49c27631026e7994 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd-auth.c,v 1.14 2026/03/11 09:10:59 dtucker Exp $ */
+/* $OpenBSD: sshd-auth.c,v 1.15 2026/05/31 11:30:50 djm Exp $ */
 /*
  * SSH2 implementation:
  * Privilege Separation:
@@ -412,14 +412,13 @@ parse_hostkeys(struct sshbuf *hostkeys)
 }
 
 static void
-recv_privsep_state(struct ssh *ssh, struct sshbuf *conf,
-    uint64_t *timing_secretp)
+recv_privsep_state(struct ssh *ssh, ServerOptions *o, uint64_t *timing_secretp)
 {
        struct sshbuf *hostkeys;
 
        debug3_f("begin");
 
-       mm_get_state(ssh, &includes, conf, NULL, timing_secretp,
+       mm_get_state(ssh, o, NULL, timing_secretp,
            &hostkeys, NULL, NULL, NULL, NULL);
        parse_hostkeys(hostkeys);
 
@@ -439,11 +438,9 @@ main(int ac, char **av)
        extern int optind;
        int r, opt, have_key = 0;
        int sock_in = -1, sock_out = -1, rexeced_flag = 0;
-       char *line;
        u_int i;
        mode_t new_umask;
        Authctxt *authctxt;
-       struct connection_info *connection_info = NULL;
        sigset_t sigmask;
        uint64_t timing_secret = 0;
 
@@ -482,17 +479,16 @@ main(int ac, char **av)
            "C:E:b:c:f:g:h:k:o:p:u:46DGQRTdeiqrtV")) != -1) {
                switch (opt) {
                case '4':
-                       options.address_family = AF_INET;
+                       /* ignore */
                        break;
                case '6':
-                       options.address_family = AF_INET6;
+                       /* ignore */
                        break;
                case 'f':
-                       config_file_name = optarg;
+                       /* ignore */
                        break;
                case 'c':
-                       servconf_add_hostcert("[command-line]", 0,
-                           &options, optarg);
+                       /* ignore */
                        break;
                case 'd':
                        if (debug_flag == 0) {
@@ -519,46 +515,28 @@ main(int ac, char **av)
                        /* ignored */
                        break;
                case 'q':
-                       options.log_level = SYSLOG_LEVEL_QUIET;
+                       /* ignored */
                        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);
-                       }
+                       /* ignored */
                        break;
                case 'g':
-                       if ((options.login_grace_time = convtime(optarg)) == -1) {
-                               fprintf(stderr, "Invalid login grace time.\n");
-                               exit(1);
-                       }
+                       /* ignored */
                        break;
                case 'k':
                        /* protocol 1, ignored */
                        break;
                case 'h':
-                       servconf_add_hostkey("[command-line]", 0,
-                           &options, optarg, 1);
+                       /* ignored */
                        break;
                case 't':
                case 'T':
                case 'G':
-                       fatal("test/dump modes not supported");
-                       break;
                case 'C':
-                       connection_info = server_get_connection_info(ssh, 0, 0);
-                       if (parse_server_match_testspec(connection_info,
-                           optarg) == -1)
-                               exit(1);
+                       fatal("test/dump modes not supported");
                        break;
                case 'u':
                        utmp_len = (u_int)strtonum(optarg, 0, HOST_NAME_MAX+1+1, NULL);
@@ -568,11 +546,7 @@ main(int ac, char **av)
                        }
                        break;
                case 'o':
-                       line = xstrdup(optarg);
-                       if (process_server_config_line(&options, line,
-                           "command-line", 0, NULL, NULL, &includes) != 0)
-                               exit(1);
-                       free(line);
+                       /* ignored */
                        break;
                case 'V':
                        fprintf(stderr, "%s, %s\n",
@@ -637,10 +611,7 @@ main(int ac, char **av)
        if ((cfg = sshbuf_new()) == NULL)
                fatal("sshbuf_new config buf failed");
        setproctitle("%s", "[session-auth early]");
-       recv_privsep_state(ssh, cfg, &timing_secret);
-       parse_server_config(&options, "rexec", cfg, &includes, NULL, 1);
-       /* Fill in default values for those options not explicitly set. */
-       fill_default_server_options(&options);
+       recv_privsep_state(ssh, &options, &timing_secret);
        options.timing_secret = timing_secret; /* XXX eliminate from unpriv */
        ssh_packet_set_qos(ssh, options.ip_qos_interactive,
            options.ip_qos_bulk);