Start the process of splitting sshd into separate binaries. This step
authordjm <djm@openbsd.org>
Fri, 17 May 2024 00:30:23 +0000 (00:30 +0000)
committerdjm <djm@openbsd.org>
Fri, 17 May 2024 00:30:23 +0000 (00:30 +0000)
splits sshd into a listener and a session binary. More splits are
planned.

After this changes, the listener binary will validate the configuration,
load the hostkeys, listen on port 22 and manage MaxStartups only. All
session handling will be performed by a new sshd-session binary that the
listener fork+execs.

This reduces the listener process to the minimum necessary and sets us
up for future work on the sshd-session binary.

feedback/ok markus@ deraadt@

NB. if you're updating via source, please restart sshd after installing,
otherwise you run the risk of locking yourself out.

35 files changed:
usr.bin/ssh/Makefile
usr.bin/ssh/Makefile.inc
usr.bin/ssh/auth-rhosts.c
usr.bin/ssh/auth.c
usr.bin/ssh/auth.h
usr.bin/ssh/auth2-gss.c
usr.bin/ssh/auth2-hostbased.c
usr.bin/ssh/auth2-kbdint.c
usr.bin/ssh/auth2-none.c
usr.bin/ssh/auth2-passwd.c
usr.bin/ssh/auth2-pubkey.c
usr.bin/ssh/auth2.c
usr.bin/ssh/channels.c
usr.bin/ssh/kex.c
usr.bin/ssh/kex.h
usr.bin/ssh/kexgexs.c
usr.bin/ssh/misc.c
usr.bin/ssh/misc.h
usr.bin/ssh/monitor.c
usr.bin/ssh/monitor.h
usr.bin/ssh/monitor_wrap.c
usr.bin/ssh/monitor_wrap.h
usr.bin/ssh/msg.c
usr.bin/ssh/packet.c
usr.bin/ssh/packet.h
usr.bin/ssh/pathnames.h
usr.bin/ssh/servconf.c
usr.bin/ssh/servconf.h
usr.bin/ssh/serverloop.c
usr.bin/ssh/session.c
usr.bin/ssh/ssh_api.c
usr.bin/ssh/sshd-session.c [new file with mode: 0644]
usr.bin/ssh/sshd-session/Makefile [new file with mode: 0644]
usr.bin/ssh/sshd.c
usr.bin/ssh/sshd/Makefile

index 7e3add9..09f8b0a 100644 (file)
@@ -1,8 +1,9 @@
-#      $OpenBSD: Makefile,v 1.17 2019/10/31 21:22:01 djm Exp $
+#      $OpenBSD: Makefile,v 1.18 2024/05/17 00:30:23 djm Exp $
 
 .include <bsd.own.mk>
 
-SUBDIR=        ssh sshd ssh-add ssh-keygen ssh-agent scp sftp-server \
+SUBDIR=        ssh sshd sshd-session \
+       ssh-add ssh-keygen ssh-agent scp sftp-server \
        ssh-keysign ssh-keyscan sftp ssh-pkcs11-helper ssh-sk-helper
 
 distribution:
index 84487e7..ae7814a 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile.inc,v 1.89 2024/01/11 01:45:36 djm Exp $
+#      $OpenBSD: Makefile.inc,v 1.90 2024/05/17 00:30:23 djm Exp $
 
 .include <bsd.own.mk>
 
@@ -25,7 +25,7 @@ CDIAGFLAGS+=  -Wstrict-aliasing=2
 CDIAGFLAGS+=   -Wold-style-definition
 .endif
 
-#CDIAGFLAGS+=  -Werror
+CDIAGFLAGS+=   -Werror
 #CDIAGFLAGS+=  -fno-common
 #DEBUG=-g
 #INSTALL_STRIP=
@@ -111,14 +111,17 @@ SRCS_KEYP+=       atomicio.c
 SRCS_KRL+=     bitmap.c
 SRCS_KRL+=     krl.c
 
+SRCS_MAC+=     mac.c
+SRCS_MAC+=     hmac.c
+SRCS_MAC+=     umac.c
+SRCS_MAC+=     umac128.c
+
 SRCS_PKT+=     canohost.c
 SRCS_PKT+=     dispatch.c
-SRCS_PKT+=     hmac.c
 SRCS_PKT+=     kex.c
-SRCS_PKT+=     mac.c
+SRCS_PKT+=     kex-names.c
 SRCS_PKT+=     packet.c
-SRCS_PKT+=     umac.c
-SRCS_PKT+=     umac128.c
+SRCS_PKT+=     ${SRCS_MAC}
 
 SRCS_PROT+=    channels.c
 SRCS_PROT+=    monitor_fdpass.c
index 07f7553..3717e93 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-rhosts.c,v 1.57 2022/12/09 00:17:40 dtucker Exp $ */
+/* $OpenBSD: auth-rhosts.c,v 1.58 2024/05/17 00:30:23 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -42,7 +42,6 @@
 
 /* import */
 extern ServerOptions options;
-extern int use_privsep;
 
 /*
  * This function processes an rhosts-style file (.rhosts, .shosts, or
index c58798d..3fec9d7 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.c,v 1.160 2023/03/05 05:34:09 dtucker Exp $ */
+/* $OpenBSD: auth.c,v 1.161 2024/05/17 00:30:23 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -67,7 +67,6 @@
 /* import */
 extern ServerOptions options;
 extern struct include_list includes;
-extern int use_privsep;
 extern struct sshauthopt *auth_opts;
 
 /* Debugging messages */
@@ -246,7 +245,7 @@ auth_log(struct ssh *ssh, int authenticated, int partial,
        const char *authmsg;
        char *extra = NULL;
 
-       if (use_privsep && !mm_is_monitor() && !authctxt->postponed)
+       if (!mm_is_monitor() && !authctxt->postponed)
                return;
 
        /* Raise logging level */
@@ -420,14 +419,14 @@ getpwnamallow(struct ssh *ssh, const char *user)
        struct connection_info *ci;
        u_int i;
 
-       ci = get_connection_info(ssh, 1, options.use_dns);
+       ci = server_get_connection_info(ssh, 1, options.use_dns);
        ci->user = user;
        parse_server_match_config(&options, &includes, ci);
        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]);
-       process_permitopen(ssh, &options);
+       server_process_permitopen(ssh);
 
        pw = getpwnam(user);
        if (pw == NULL) {
@@ -562,93 +561,6 @@ fakepw(void)
        return (&fake);
 }
 
-/*
- * Returns the remote DNS hostname as a string. The returned string must not
- * be freed. NB. this will usually trigger a DNS query the first time it is
- * called.
- * This function does additional checks on the hostname to mitigate some
- * attacks on based on conflation of hostnames and IP addresses.
- */
-
-static char *
-remote_hostname(struct ssh *ssh)
-{
-       struct sockaddr_storage from;
-       socklen_t fromlen;
-       struct addrinfo hints, *ai, *aitop;
-       char name[NI_MAXHOST], ntop2[NI_MAXHOST];
-       const char *ntop = ssh_remote_ipaddr(ssh);
-
-       /* Get IP address of client. */
-       fromlen = sizeof(from);
-       memset(&from, 0, sizeof(from));
-       if (getpeername(ssh_packet_get_connection_in(ssh),
-           (struct sockaddr *)&from, &fromlen) == -1) {
-               debug("getpeername failed: %.100s", strerror(errno));
-               return xstrdup(ntop);
-       }
-
-       debug3("Trying to reverse map address %.100s.", ntop);
-       /* Map the IP address to a host name. */
-       if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
-           NULL, 0, NI_NAMEREQD) != 0) {
-               /* Host name not found.  Use ip address. */
-               return xstrdup(ntop);
-       }
-
-       /*
-        * if reverse lookup result looks like a numeric hostname,
-        * someone is trying to trick us by PTR record like following:
-        *      1.1.1.10.in-addr.arpa.  IN PTR  2.3.4.5
-        */
-       memset(&hints, 0, sizeof(hints));
-       hints.ai_socktype = SOCK_DGRAM; /*dummy*/
-       hints.ai_flags = AI_NUMERICHOST;
-       if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
-               logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
-                   name, ntop);
-               freeaddrinfo(ai);
-               return xstrdup(ntop);
-       }
-
-       /* Names are stored in lowercase. */
-       lowercase(name);
-
-       /*
-        * Map it back to an IP address and check that the given
-        * address actually is an address of this host.  This is
-        * necessary because anyone with access to a name server can
-        * define arbitrary names for an IP address. Mapping from
-        * name to IP address can be trusted better (but can still be
-        * fooled if the intruder has access to the name server of
-        * the domain).
-        */
-       memset(&hints, 0, sizeof(hints));
-       hints.ai_family = from.ss_family;
-       hints.ai_socktype = SOCK_STREAM;
-       if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
-               logit("reverse mapping checking getaddrinfo for %.700s "
-                   "[%s] failed.", name, ntop);
-               return xstrdup(ntop);
-       }
-       /* Look for the address from the list of addresses. */
-       for (ai = aitop; ai; ai = ai->ai_next) {
-               if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
-                   sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
-                   (strcmp(ntop, ntop2) == 0))
-                               break;
-       }
-       freeaddrinfo(aitop);
-       /* If we reached the end of the list, the address was not there. */
-       if (ai == NULL) {
-               /* Address not found for the host name. */
-               logit("Address %.100s maps to %.600s, but this does not "
-                   "map back to the address.", ntop, name);
-               return xstrdup(ntop);
-       }
-       return xstrdup(name);
-}
-
 /*
  * Return the canonical name of the host in the other side of the current
  * connection.  The host name is cached, so it is efficient to call this
@@ -662,12 +574,10 @@ auth_get_canonical_hostname(struct ssh *ssh, int use_dns)
 
        if (!use_dns)
                return ssh_remote_ipaddr(ssh);
-       else if (dnsname != NULL)
-               return dnsname;
-       else {
-               dnsname = remote_hostname(ssh);
+       if (dnsname != NULL)
                return dnsname;
-       }
+       dnsname = ssh_remote_hostname(ssh);
+       return dnsname;
 }
 
 /* These functions link key/cert options to the auth framework */
index 54c3553..669f72e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.h,v 1.106 2022/06/15 16:08:25 djm Exp $ */
+/* $OpenBSD: auth.h,v 1.107 2024/05/17 00:30:23 djm Exp $ */
 
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
@@ -95,11 +95,15 @@ struct Authctxt {
  * the client.
  */
 
+struct authmethod_cfg {
+       const char *name;
+       const char *synonym;
+       int *enabled;
+};
+
 struct Authmethod {
-       char    *name;
-       char    *synonym;
+       struct authmethod_cfg *cfg;
        int     (*userauth)(struct ssh *, const char *);
-       int     *enabled;
 };
 
 /*
index 7bab9dd..fcb2909 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-gss.c,v 1.34 2023/03/31 04:22:27 djm Exp $ */
+/* $OpenBSD: auth2-gss.c,v 1.35 2024/05/17 00:30:23 djm Exp $ */
 
 /*
  * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
@@ -46,6 +46,7 @@
 #define SSH_GSSAPI_MAX_MECHS   2048
 
 extern ServerOptions options;
+extern struct authmethod_cfg methodcfg_gssapi;
 
 static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh);
 static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
@@ -111,7 +112,7 @@ userauth_gssapi(struct ssh *ssh, const char *method)
                return (0);
        }
 
-       if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) {
+       if (GSS_ERROR(mm_ssh_gssapi_server_ctx(&ctxt, &goid))) {
                if (ctxt != NULL)
                        ssh_gssapi_delete_ctx(&ctxt);
                free(doid);
@@ -148,7 +149,7 @@ input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh)
        size_t len;
        int r;
 
-       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+       if (authctxt == NULL)
                fatal("No authentication or GSSAPI context");
 
        gssctxt = authctxt->methoddata;
@@ -158,8 +159,8 @@ input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh)
 
        recv_tok.value = p;
        recv_tok.length = len;
-       maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
-           &send_tok, &flags));
+       maj_status = mm_ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
+           &send_tok, &flags);
 
        free(p);
 
@@ -212,7 +213,7 @@ input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh)
        u_char *p;
        size_t len;
 
-       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+       if (authctxt == NULL)
                fatal("No authentication or GSSAPI context");
 
        gssctxt = authctxt->methoddata;
@@ -223,8 +224,8 @@ input_gssapi_errtok(int type, u_int32_t plen, struct ssh *ssh)
        recv_tok.length = len;
 
        /* Push the error token into GSSAPI to see what it says */
-       maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
-           &send_tok, NULL));
+       maj_status = mm_ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
+           &send_tok, NULL);
 
        free(recv_tok.value);
 
@@ -251,7 +252,7 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
        int r, authenticated;
        const char *displayname;
 
-       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+       if (authctxt == NULL)
                fatal("No authentication or GSSAPI context");
 
        /*
@@ -262,11 +263,7 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
        if ((r = sshpkt_get_end(ssh)) != 0)
                fatal_fr(r, "parse packet");
 
-       authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
-
-       if ((!use_privsep || mm_is_monitor()) &&
-           (displayname = ssh_gssapi_displayname()) != NULL)
-               auth2_record_info(authctxt, "%s", displayname);
+       authenticated = mm_ssh_gssapi_userok(authctxt->user);
 
        authctxt->postponed = 0;
        ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
@@ -289,7 +286,7 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
        u_char *p;
        size_t len;
 
-       if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep))
+       if (authctxt == NULL)
                fatal("No authentication or GSSAPI context");
 
        gssctxt = authctxt->methoddata;
@@ -307,18 +304,14 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
                fatal_f("sshbuf_mutable_ptr failed");
        gssbuf.length = sshbuf_len(b);
 
-       if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic))))
-               authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
+       if (!GSS_ERROR(mm_ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))
+               authenticated = mm_ssh_gssapi_userok(authctxt->user);
        else
                logit("GSSAPI MIC check failed");
 
        sshbuf_free(b);
        free(mic.value);
 
-       if ((!use_privsep || mm_is_monitor()) &&
-           (displayname = ssh_gssapi_displayname()) != NULL)
-               auth2_record_info(authctxt, "%s", displayname);
-
        authctxt->postponed = 0;
        ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
        ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
@@ -329,9 +322,7 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
 }
 
 Authmethod method_gssapi = {
-       "gssapi-with-mic",
-       NULL,
+       &methodcfg_gssapi,
        userauth_gssapi,
-       &options.gss_authentication
 };
 #endif
index 644d7be..e221417 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-hostbased.c,v 1.52 2023/03/05 05:34:09 dtucker Exp $ */
+/* $OpenBSD: auth2-hostbased.c,v 1.53 2024/05/17 00:30:23 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -53,6 +53,7 @@
 
 /* import */
 extern ServerOptions options;
+extern struct authmethod_cfg methodcfg_hostbased;
 
 static int
 userauth_hostbased(struct ssh *ssh, const char *method)
@@ -144,10 +145,10 @@ userauth_hostbased(struct ssh *ssh, const char *method)
 
        /* test for allowed key and correct signature */
        authenticated = 0;
-       if (PRIVSEP(hostbased_key_allowed(ssh, authctxt->pw, cuser,
-           chost, key)) &&
-           PRIVSEP(sshkey_verify(key, sig, slen,
-           sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat, NULL)) == 0)
+       if (mm_hostbased_key_allowed(ssh, authctxt->pw, cuser,
+           chost, key) &&
+           mm_sshkey_verify(key, sig, slen,
+           sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat, NULL) == 0)
                authenticated = 1;
 
        auth2_record_key(authctxt, authenticated, key);
@@ -251,8 +252,6 @@ hostbased_key_allowed(struct ssh *ssh, struct passwd *pw,
 }
 
 Authmethod method_hostbased = {
-       "hostbased",
-       NULL,
+       &methodcfg_hostbased,
        userauth_hostbased,
-       &options.hostbased_authentication
 };
index 034b6ea..cb1cef7 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-kbdint.c,v 1.14 2021/12/19 22:12:07 djm Exp $ */
+/* $OpenBSD: auth2-kbdint.c,v 1.15 2024/05/17 00:30:23 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -40,6 +40,7 @@
 
 /* import */
 extern ServerOptions options;
+extern struct authmethod_cfg methodcfg_kbdint;
 
 static int
 userauth_kbdint(struct ssh *ssh, const char *method)
@@ -63,8 +64,6 @@ userauth_kbdint(struct ssh *ssh, const char *method)
 }
 
 Authmethod method_kbdint = {
-       "keyboard-interactive",
-       NULL,
+       &methodcfg_kbdint,
        userauth_kbdint,
-       &options.kbd_interactive_authentication
 };
index d34f654..ae4e8a3 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-none.c,v 1.25 2023/03/05 05:34:09 dtucker Exp $ */
+/* $OpenBSD: auth2-none.c,v 1.26 2024/05/17 00:30:23 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -44,9 +44,9 @@
 
 /* import */
 extern ServerOptions options;
+extern struct authmethod_cfg methodcfg_none;
 
-/* "none" is allowed only one time */
-static int none_enabled = 1;
+extern int none_enabled;
 
 static int
 userauth_none(struct ssh *ssh, const char *method)
@@ -57,13 +57,11 @@ userauth_none(struct ssh *ssh, const char *method)
        if ((r = sshpkt_get_end(ssh)) != 0)
                fatal_fr(r, "parse packet");
        if (options.permit_empty_passwd && options.password_authentication)
-               return (PRIVSEP(auth_password(ssh, "")));
+               return mm_auth_password(ssh, "");
        return (0);
 }
 
 Authmethod method_none = {
-       "none",
-       NULL,
+       &methodcfg_none,
        userauth_none,
-       &none_enabled
 };
index b9feb71..dffbb4a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-passwd.c,v 1.21 2022/05/27 04:29:40 dtucker Exp $ */
+/* $OpenBSD: auth2-passwd.c,v 1.22 2024/05/17 00:30:23 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -45,6 +45,7 @@
 
 /* import */
 extern ServerOptions options;
+extern struct authmethod_cfg methodcfg_passwd;
 
 static int
 userauth_passwd(struct ssh *ssh, const char *method)
@@ -64,15 +65,13 @@ userauth_passwd(struct ssh *ssh, const char *method)
 
        if (change)
                logit("password change not supported");
-       else if (PRIVSEP(auth_password(ssh, password)) == 1)
+       else if (mm_auth_password(ssh, password) == 1)
                authenticated = 1;
        freezero(password, len);
        return authenticated;
 }
 
 Authmethod method_passwd = {
-       "password",
-       NULL,
+       &methodcfg_passwd,
        userauth_passwd,
-       &options.password_authentication
 };
index 1e536b1..d459e10 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-pubkey.c,v 1.119 2023/07/27 22:25:17 djm Exp $ */
+/* $OpenBSD: auth2-pubkey.c,v 1.120 2024/05/17 00:30:23 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2010 Damien Miller.  All rights reserved.
@@ -69,6 +69,7 @@
 
 /* import */
 extern ServerOptions options;
+extern struct authmethod_cfg methodcfg_pubkey;
 
 static char *
 format_key(const struct sshkey *key)
@@ -216,11 +217,11 @@ userauth_pubkey(struct ssh *ssh, const char *method)
 #endif
                /* test for correct signature */
                authenticated = 0;
-               if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) &&
-                   PRIVSEP(sshkey_verify(key, sig, slen,
+               if (mm_user_key_allowed(ssh, pw, key, 1, &authopts) &&
+                   mm_sshkey_verify(key, sig, slen,
                    sshbuf_ptr(b), sshbuf_len(b),
                    (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL,
-                   ssh->compat, &sig_details)) == 0) {
+                   ssh->compat, &sig_details) == 0) {
                        authenticated = 1;
                }
                if (authenticated == 1 && sig_details != NULL) {
@@ -278,7 +279,7 @@ userauth_pubkey(struct ssh *ssh, const char *method)
                 * if a user is not allowed to login. is this an
                 * issue? -markus
                 */
-               if (PRIVSEP(user_key_allowed(ssh, pw, key, 0, NULL))) {
+               if (mm_user_key_allowed(ssh, pw, key, 0, NULL)) {
                        if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK))
                            != 0 ||
                            (r = sshpkt_put_cstring(ssh, pkalg)) != 0 ||
@@ -810,8 +811,6 @@ user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
 }
 
 Authmethod method_pubkey = {
-       "publickey",
-       "publickey-hostbound-v00@openssh.com",
+       &methodcfg_pubkey,
        userauth_pubkey,
-       &options.pubkey_authentication
 };
index 40bd28a..86ee787 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2.c,v 1.168 2023/12/18 14:45:49 djm Exp $ */
+/* $OpenBSD: auth2.c,v 1.169 2024/05/17 00:30:23 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -141,7 +141,7 @@ userauth_banner(struct ssh *ssh)
        if (options.banner == NULL)
                return;
 
-       if ((banner = PRIVSEP(auth2_read_banner())) == NULL)
+       if ((banner = mm_auth2_read_banner()) == NULL)
                goto done;
 
        if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_BANNER)) != 0 ||
@@ -281,7 +281,7 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
                auth_maxtries_exceeded(ssh);
        if (authctxt->attempt++ == 0) {
                /* setup auth context */
-               authctxt->pw = PRIVSEP(getpwnamallow(ssh, user));
+               authctxt->pw = mm_getpwnamallow(ssh, user);
                if (authctxt->pw && strcmp(service, "ssh-connection")==0) {
                        authctxt->valid = 1;
                        debug2_f("setting up authctxt for %s", user);
@@ -292,13 +292,11 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
                }
                ssh_packet_set_log_preamble(ssh, "%suser %s",
                    authctxt->valid ? "authenticating " : "invalid ", user);
-               setproctitle("%s%s", authctxt->valid ? user : "unknown",
-                   use_privsep ? " [net]" : "");
+               setproctitle("%s [net]", authctxt->valid ? user : "unknown");
                authctxt->user = xstrdup(user);
                authctxt->service = xstrdup(service);
                authctxt->style = style ? xstrdup(style) : NULL;
-               if (use_privsep)
-                       mm_inform_authserv(service, style);
+               mm_inform_authserv(service, style);
                userauth_banner(ssh);
                if ((r = kex_server_update_ext_info(ssh)) != 0)
                        fatal_fr(r, "kex_server_update_ext_info failed");
@@ -362,7 +360,7 @@ userauth_finish(struct ssh *ssh, int authenticated, const char *packet_method,
                /* prefer primary authmethod name to possible synonym */
                if ((m = authmethod_byname(method)) == NULL)
                        fatal("INTERNAL ERROR: bad method %s", method);
-               method = m->name;
+               method = m->cfg->name;
        }
 
        /* Special handling for root */
@@ -453,16 +451,16 @@ authmethods_get(Authctxt *authctxt)
        if ((b = sshbuf_new()) == NULL)
                fatal_f("sshbuf_new failed");
        for (i = 0; authmethods[i] != NULL; i++) {
-               if (strcmp(authmethods[i]->name, "none") == 0)
+               if (strcmp(authmethods[i]->cfg->name, "none") == 0)
                        continue;
-               if (authmethods[i]->enabled == NULL ||
-                   *(authmethods[i]->enabled) == 0)
+               if (authmethods[i]->cfg->enabled == NULL ||
+                   *(authmethods[i]->cfg->enabled) == 0)
                        continue;
-               if (!auth2_method_allowed(authctxt, authmethods[i]->name,
+               if (!auth2_method_allowed(authctxt, authmethods[i]->cfg->name,
                    NULL))
                        continue;
                if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) ? "," : "",
-                   authmethods[i]->name)) != 0)
+                   authmethods[i]->cfg->name)) != 0)
                        fatal_fr(r, "buffer error");
        }
        if ((list = sshbuf_dup_string(b)) == NULL)
@@ -479,9 +477,9 @@ authmethod_byname(const char *name)
        if (name == NULL)
                fatal_f("NULL authentication method name");
        for (i = 0; authmethods[i] != NULL; i++) {
-               if (strcmp(name, authmethods[i]->name) == 0 ||
-                   (authmethods[i]->synonym != NULL &&
-                   strcmp(name, authmethods[i]->synonym) == 0))
+               if (strcmp(name, authmethods[i]->cfg->name) == 0 ||
+                   (authmethods[i]->cfg->synonym != NULL &&
+                   strcmp(name, authmethods[i]->cfg->synonym) == 0))
                        return authmethods[i];
        }
        debug_f("unrecognized authentication method name: %s", name);
@@ -496,11 +494,11 @@ authmethod_lookup(Authctxt *authctxt, const char *name)
        if ((method = authmethod_byname(name)) == NULL)
                return NULL;
 
-       if (method->enabled == NULL || *(method->enabled) == 0) {
+       if (method->cfg->enabled == NULL || *(method->cfg->enabled) == 0) {
                debug3_f("method %s not enabled", name);
                return NULL;
        }
-       if (!auth2_method_allowed(authctxt, method->name, NULL)) {
+       if (!auth2_method_allowed(authctxt, method->cfg->name, NULL)) {
                debug3_f("method %s not allowed "
                    "by AuthenticationMethods", name);
                return NULL;
@@ -508,53 +506,6 @@ authmethod_lookup(Authctxt *authctxt, const char *name)
        return method;
 }
 
-/*
- * Check a comma-separated list of methods for validity. Is need_enable is
- * non-zero, then also require that the methods are enabled.
- * Returns 0 on success or -1 if the methods list is invalid.
- */
-int
-auth2_methods_valid(const char *_methods, int need_enable)
-{
-       char *methods, *omethods, *method, *p;
-       u_int i, found;
-       int ret = -1;
-
-       if (*_methods == '\0') {
-               error("empty authentication method list");
-               return -1;
-       }
-       omethods = methods = xstrdup(_methods);
-       while ((method = strsep(&methods, ",")) != NULL) {
-               for (found = i = 0; !found && authmethods[i] != NULL; i++) {
-                       if ((p = strchr(method, ':')) != NULL)
-                               *p = '\0';
-                       if (strcmp(method, authmethods[i]->name) != 0)
-                               continue;
-                       if (need_enable) {
-                               if (authmethods[i]->enabled == NULL ||
-                                   *(authmethods[i]->enabled) == 0) {
-                                       error("Disabled method \"%s\" in "
-                                           "AuthenticationMethods list \"%s\"",
-                                           method, _methods);
-                                       goto out;
-                               }
-                       }
-                       found = 1;
-                       break;
-               }
-               if (!found) {
-                       error("Unknown authentication method \"%s\" in list",
-                           method);
-                       goto out;
-               }
-       }
-       ret = 0;
- out:
-       free(omethods);
-       return ret;
-}
-
 /*
  * Prune the AuthenticationMethods supplied in the configuration, removing
  * any methods lists that include disabled methods. Note that this might
index d07232d..281cecf 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.437 2024/03/06 02:59:59 djm Exp $ */
+/* $OpenBSD: channels.c,v 1.438 2024/05/17 00:30:23 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 /* -- agent forwarding */
 #define        NUM_SOCKS       10
 
-/* -- tcp forwarding */
-/* special-case port number meaning allow any port */
-#define FWD_PERMIT_ANY_PORT    0
-
-/* special-case wildcard meaning allow any host */
-#define FWD_PERMIT_ANY_HOST    "*"
-
 /* -- X11 forwarding */
 /* Maximum number of fake X11 displays to try. */
 #define MAX_DISPLAYS  1000
@@ -4530,19 +4523,6 @@ channel_update_permission(struct ssh *ssh, int idx, int newport)
        }
 }
 
-/* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */
-int
-permitopen_port(const char *p)
-{
-       int port;
-
-       if (strcmp(p, "*") == 0)
-               return FWD_PERMIT_ANY_PORT;
-       if ((port = a2port(p)) > 0)
-               return port;
-       return -1;
-}
-
 /* Try to start non-blocking connect to next host in cctx list */
 static int
 connect_next(struct channel_connect *cctx)
index 1e9ad51..a99d050 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.c,v 1.185 2024/01/08 00:34:33 djm Exp $ */
+/* $OpenBSD: kex.c,v 1.186 2024/05/17 00:30:23 djm Exp $ */
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
  *
@@ -76,244 +76,6 @@ static const char * const proposal_names[PROPOSAL_MAX] = {
        "languages stoc",
 };
 
-struct kexalg {
-       char *name;
-       u_int type;
-       int ec_nid;
-       int hash_alg;
-};
-static const struct kexalg kexalgs[] = {
-#ifdef WITH_OPENSSL
-       { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
-       { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
-       { KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 },
-       { KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 },
-       { KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 },
-       { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
-       { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
-       { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
-           NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
-       { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
-           SSH_DIGEST_SHA384 },
-       { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
-           SSH_DIGEST_SHA512 },
-#endif
-       { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
-       { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
-       { KEX_SNTRUP761X25519_SHA512, KEX_KEM_SNTRUP761X25519_SHA512, 0,
-           SSH_DIGEST_SHA512 },
-       { NULL, 0, -1, -1},
-};
-
-char *
-kex_alg_list(char sep)
-{
-       char *ret = NULL, *tmp;
-       size_t nlen, rlen = 0;
-       const struct kexalg *k;
-
-       for (k = kexalgs; k->name != NULL; k++) {
-               if (ret != NULL)
-                       ret[rlen++] = sep;
-               nlen = strlen(k->name);
-               if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
-                       free(ret);
-                       return NULL;
-               }
-               ret = tmp;
-               memcpy(ret + rlen, k->name, nlen + 1);
-               rlen += nlen;
-       }
-       return ret;
-}
-
-static const struct kexalg *
-kex_alg_by_name(const char *name)
-{
-       const struct kexalg *k;
-
-       for (k = kexalgs; k->name != NULL; k++) {
-               if (strcmp(k->name, name) == 0)
-                       return k;
-       }
-       return NULL;
-}
-
-/* Validate KEX method name list */
-int
-kex_names_valid(const char *names)
-{
-       char *s, *cp, *p;
-
-       if (names == NULL || strcmp(names, "") == 0)
-               return 0;
-       if ((s = cp = strdup(names)) == NULL)
-               return 0;
-       for ((p = strsep(&cp, ",")); p && *p != '\0';
-           (p = strsep(&cp, ","))) {
-               if (kex_alg_by_name(p) == NULL) {
-                       error("Unsupported KEX algorithm \"%.100s\"", p);
-                       free(s);
-                       return 0;
-               }
-       }
-       debug3("kex names ok: [%s]", names);
-       free(s);
-       return 1;
-}
-
-/* returns non-zero if proposal contains any algorithm from algs */
-static int
-has_any_alg(const char *proposal, const char *algs)
-{
-       char *cp;
-
-       if ((cp = match_list(proposal, algs, NULL)) == NULL)
-               return 0;
-       free(cp);
-       return 1;
-}
-
-/*
- * Concatenate algorithm names, avoiding duplicates in the process.
- * Caller must free returned string.
- */
-char *
-kex_names_cat(const char *a, const char *b)
-{
-       char *ret = NULL, *tmp = NULL, *cp, *p;
-       size_t len;
-
-       if (a == NULL || *a == '\0')
-               return strdup(b);
-       if (b == NULL || *b == '\0')
-               return strdup(a);
-       if (strlen(b) > 1024*1024)
-               return NULL;
-       len = strlen(a) + strlen(b) + 2;
-       if ((tmp = cp = strdup(b)) == NULL ||
-           (ret = calloc(1, len)) == NULL) {
-               free(tmp);
-               return NULL;
-       }
-       strlcpy(ret, a, len);
-       for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {
-               if (has_any_alg(ret, p))
-                       continue; /* Algorithm already present */
-               if (strlcat(ret, ",", len) >= len ||
-                   strlcat(ret, p, len) >= len) {
-                       free(tmp);
-                       free(ret);
-                       return NULL; /* Shouldn't happen */
-               }
-       }
-       free(tmp);
-       return ret;
-}
-
-/*
- * Assemble a list of algorithms from a default list and a string from a
- * configuration file. The user-provided string may begin with '+' to
- * indicate that it should be appended to the default, '-' that the
- * specified names should be removed, or '^' that they should be placed
- * at the head.
- */
-int
-kex_assemble_names(char **listp, const char *def, const char *all)
-{
-       char *cp, *tmp, *patterns;
-       char *list = NULL, *ret = NULL, *matching = NULL, *opatterns = NULL;
-       int r = SSH_ERR_INTERNAL_ERROR;
-
-       if (listp == NULL || def == NULL || all == NULL)
-               return SSH_ERR_INVALID_ARGUMENT;
-
-       if (*listp == NULL || **listp == '\0') {
-               if ((*listp = strdup(def)) == NULL)
-                       return SSH_ERR_ALLOC_FAIL;
-               return 0;
-       }
-
-       list = *listp;
-       *listp = NULL;
-       if (*list == '+') {
-               /* Append names to default list */
-               if ((tmp = kex_names_cat(def, list + 1)) == NULL) {
-                       r = SSH_ERR_ALLOC_FAIL;
-                       goto fail;
-               }
-               free(list);
-               list = tmp;
-       } else if (*list == '-') {
-               /* Remove names from default list */
-               if ((*listp = match_filter_denylist(def, list + 1)) == NULL) {
-                       r = SSH_ERR_ALLOC_FAIL;
-                       goto fail;
-               }
-               free(list);
-               /* filtering has already been done */
-               return 0;
-       } else if (*list == '^') {
-               /* Place names at head of default list */
-               if ((tmp = kex_names_cat(list + 1, def)) == NULL) {
-                       r = SSH_ERR_ALLOC_FAIL;
-                       goto fail;
-               }
-               free(list);
-               list = tmp;
-       } else {
-               /* Explicit list, overrides default - just use "list" as is */
-       }
-
-       /*
-        * The supplied names may be a pattern-list. For the -list case,
-        * the patterns are applied above. For the +list and explicit list
-        * cases we need to do it now.
-        */
-       ret = NULL;
-       if ((patterns = opatterns = strdup(list)) == NULL) {
-               r = SSH_ERR_ALLOC_FAIL;
-               goto fail;
-       }
-       /* Apply positive (i.e. non-negated) patterns from the list */
-       while ((cp = strsep(&patterns, ",")) != NULL) {
-               if (*cp == '!') {
-                       /* negated matches are not supported here */
-                       r = SSH_ERR_INVALID_ARGUMENT;
-                       goto fail;
-               }
-               free(matching);
-               if ((matching = match_filter_allowlist(all, cp)) == NULL) {
-                       r = SSH_ERR_ALLOC_FAIL;
-                       goto fail;
-               }
-               if ((tmp = kex_names_cat(ret, matching)) == NULL) {
-                       r = SSH_ERR_ALLOC_FAIL;
-                       goto fail;
-               }
-               free(ret);
-               ret = tmp;
-       }
-       if (ret == NULL || *ret == '\0') {
-               /* An empty name-list is an error */
-               /* XXX better error code? */
-               r = SSH_ERR_INVALID_ARGUMENT;
-               goto fail;
-       }
-
-       /* success */
-       *listp = ret;
-       ret = NULL;
-       r = 0;
-
- fail:
-       free(matching);
-       free(opatterns);
-       free(list);
-       free(ret);
-       return r;
-}
-
 /*
  * Fill out a proposal array with dynamically allocated values, which may
  * be modified as required for compatibility reasons.
@@ -512,11 +274,11 @@ kex_set_server_sig_algs(struct ssh *ssh, const char *allowed_algs)
            (alg = strsep(&algs, ","))) {
                if ((sigalg = sshkey_sigalg_by_name(alg)) == NULL)
                        continue;
-               if (!has_any_alg(sigalg, sigalgs))
+               if (!kex_has_any_alg(sigalg, sigalgs))
                        continue;
                /* Don't add an algorithm twice. */
                if (ssh->kex->server_sig_algs != NULL &&
-                   has_any_alg(sigalg, ssh->kex->server_sig_algs))
+                   kex_has_any_alg(sigalg, ssh->kex->server_sig_algs))
                        continue;
                xextendf(&ssh->kex->server_sig_algs, ",", "%s", sigalg);
        }
@@ -1091,20 +853,18 @@ choose_comp(struct sshcomp *comp, char *client, char *server)
 static int
 choose_kex(struct kex *k, char *client, char *server)
 {
-       const struct kexalg *kexalg;
-
        k->name = match_list(client, server, NULL);
 
        debug("kex: algorithm: %s", k->name ? k->name : "(no match)");
        if (k->name == NULL)
                return SSH_ERR_NO_KEX_ALG_MATCH;
-       if ((kexalg = kex_alg_by_name(k->name)) == NULL) {
+       if (!kex_name_valid(k->name)) {
                error_f("unsupported KEX method %s", k->name);
                return SSH_ERR_INTERNAL_ERROR;
        }
-       k->kex_type = kexalg->type;
-       k->hash_alg = kexalg->hash_alg;
-       k->ec_nid = kexalg->ec_nid;
+       k->kex_type = kex_type_from_name(k->name);
+       k->hash_alg = kex_hash_from_name(k->name);
+       k->ec_nid = kex_nid_from_name(k->name);
        return 0;
 }
 
@@ -1154,7 +914,7 @@ proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
 static int
 kexalgs_contains(char **peer, const char *ext)
 {
-       return has_any_alg(peer[PROPOSAL_KEX_ALGS], ext);
+       return kex_has_any_alg(peer[PROPOSAL_KEX_ALGS], ext);
 }
 
 static int
@@ -1205,10 +965,10 @@ kex_choose_conf(struct ssh *ssh, uint32_t seq)
 
        /* Check whether client supports rsa-sha2 algorithms */
        if (kex->server && (kex->flags & KEX_INITIAL)) {
-               if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
+               if (kex_has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
                    "rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com"))
                        kex->flags |= KEX_RSA_SHA2_256_SUPPORTED;
-               if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
+               if (kex_has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS],
                    "rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com"))
                        kex->flags |= KEX_RSA_SHA2_512_SUPPORTED;
        }
index 59aebf3..ca8d5aa 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: kex.h,v 1.122 2024/02/02 00:13:34 djm Exp $ */
+/* $OpenBSD: kex.h,v 1.123 2024/05/17 00:30:23 djm Exp $ */
 
 /*
  * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
@@ -85,7 +85,7 @@ enum kex_modes {
 };
 
 enum kex_exchange {
-       KEX_DH_GRP1_SHA1,
+       KEX_DH_GRP1_SHA1 = 1,
        KEX_DH_GRP14_SHA1,
        KEX_DH_GRP14_SHA256,
        KEX_DH_GRP16_SHA512,
@@ -176,9 +176,14 @@ struct kex {
        struct sshbuf *client_pub;
 };
 
+int     kex_name_valid(const char *);
+u_int   kex_type_from_name(const char *);
+int     kex_hash_from_name(const char *);
+int     kex_nid_from_name(const char *);
 int     kex_names_valid(const char *);
 char   *kex_alg_list(char);
 char   *kex_names_cat(const char *, const char *);
+int     kex_has_any_alg(const char *, const char *);
 int     kex_assemble_names(char **, const char *, const char *);
 void    kex_proposal_populate_entries(struct ssh *, char *prop[PROPOSAL_MAX],
     const char *, const char *, const char *, const char *, const char *);
index ea83bb2..1f7e264 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: kexgexs.c,v 1.46 2023/03/29 01:07:48 dtucker Exp $ */
+/* $OpenBSD: kexgexs.c,v 1.47 2024/05/17 00:30:23 djm Exp $ */
 /*
  * Copyright (c) 2000 Niels Provos.  All rights reserved.
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
@@ -91,7 +91,7 @@ input_kex_dh_gex_request(int type, u_int32_t seq, struct ssh *ssh)
        }
 
        /* Contact privileged parent */
-       kex->dh = PRIVSEP(choose_dh(min, nbits, max));
+       kex->dh = mm_choose_dh(min, nbits, max);
        if (kex->dh == NULL) {
                (void)sshpkt_disconnect(ssh, "no matching DH grp found");
                r = SSH_ERR_ALLOC_FAIL;
index 36b1cbb..59127b6 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.c,v 1.193 2024/04/02 10:02:08 deraadt Exp $ */
+/* $OpenBSD: misc.c,v 1.194 2024/05/17 00:30:23 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2005-2020 Damien Miller.  All rights reserved.
@@ -1909,6 +1909,19 @@ forward_equals(const struct Forward *a, const struct Forward *b)
        return 1;
 }
 
+/* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */
+int
+permitopen_port(const char *p)
+{
+       int port;
+
+       if (strcmp(p, "*") == 0)
+               return FWD_PERMIT_ANY_PORT;
+       if ((port = a2port(p)) > 0)
+               return port;
+       return -1;
+}
+
 /* returns 1 if process is already daemonized, 0 otherwise */
 int
 daemonized(void)
index d460dc7..00b8bc5 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.h,v 1.107 2024/03/04 02:16:11 djm Exp $ */
+/* $OpenBSD: misc.h,v 1.108 2024/05/17 00:30:24 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
 #include <stdio.h>
 #include <signal.h>
 
+/* special-case port number meaning allow any port */
+#define FWD_PERMIT_ANY_PORT    0
+
+/* special-case wildcard meaning allow any host */
+#define FWD_PERMIT_ANY_HOST    "*"
+
 /* Data structure for representing a forwarding request. */
 struct Forward {
        char     *listen_host;          /* Host (address) to listen on. */
@@ -34,6 +40,8 @@ struct Forward {
 };
 
 int forward_equals(const struct Forward *, const struct Forward *);
+int permitopen_port(const char *p);
+
 int daemonized(void);
 
 /* Common server and client forwarding options. */
index 8746a8e..6c8fc6f 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.237 2023/08/16 16:14:11 djm Exp $ */
+/* $OpenBSD: monitor.c,v 1.238 2024/05/17 00:30:24 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -641,13 +641,39 @@ mm_answer_sign(struct ssh *ssh, int sock, struct sshbuf *m)
                        fatal_fr(r, "assemble %s", #id); \
        } while (0)
 
+void
+mm_encode_server_options(struct sshbuf *m)
+{
+       int r;
+       u_int i;
+
+       /* XXX this leaks raw pointers to the unpriv child processes */
+       if ((r = sshbuf_put_string(m, &options, sizeof(options))) != 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) 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
+}
+
 /* Retrieves the password entry and also checks if the user is permitted */
 int
 mm_answer_pwnamallow(struct ssh *ssh, int sock, struct sshbuf *m)
 {
        struct passwd *pwent;
        int r, allowed = 0;
-       u_int i;
 
        debug3_f("entering");
 
@@ -692,24 +718,9 @@ mm_answer_pwnamallow(struct ssh *ssh, int sock, struct sshbuf *m)
  out:
        ssh_packet_set_log_preamble(ssh, "%suser %s",
            authctxt->valid ? "authenticating" : "invalid ", authctxt->user);
-       if ((r = sshbuf_put_string(m, &options, sizeof(options))) != 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) 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
+       /* Send active options to unpriv */
+       mm_encode_server_options(m);
 
        /* Create valid auth method lists */
        if (auth2_setup_methods_lists(authctxt) != 0) {
index 15402c4..7049e05 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.h,v 1.23 2019/01/19 21:43:56 djm Exp $ */
+/* $OpenBSD: monitor.h,v 1.24 2024/05/17 00:30:24 djm Exp $ */
 
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
@@ -59,6 +59,7 @@ enum monitor_reqtype {
 };
 
 struct ssh;
+struct sshbuf;
 
 struct monitor {
        int                      m_recvfd;
@@ -85,4 +86,7 @@ void mm_request_receive(int, struct sshbuf *);
 void mm_request_receive_expect(int, enum monitor_reqtype, struct sshbuf *);
 void mm_get_keystate(struct ssh *, struct monitor *);
 
+/* XXX: should be returned via a monitor call rather than config_fd */
+void mm_encode_server_options(struct sshbuf *);
+
 #endif /* _MONITOR_H_ */
index 3115dfe..6287a8c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor_wrap.c,v 1.129 2023/12/18 14:45:49 djm Exp $ */
+/* $OpenBSD: monitor_wrap.c,v 1.130 2024/05/17 00:30:24 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -61,7 +61,6 @@
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
-#include "monitor_wrap.h"
 #include "atomicio.h"
 #include "monitor_fdpass.h"
 #include "misc.h"
@@ -69,6 +68,7 @@
 #include "channels.h"
 #include "session.h"
 #include "servconf.h"
+#include "monitor_wrap.h"
 
 #include "ssherr.h"
 
@@ -143,8 +143,10 @@ mm_request_receive(int sock, struct sshbuf *m)
        debug3_f("entering");
 
        if (atomicio(read, sock, buf, sizeof(buf)) != sizeof(buf)) {
-               if (errno == EPIPE)
+               if (errno == EPIPE) {
+                       debug3_f("monitor fd closed");
                        cleanup_exit(255);
+               }
                fatal_f("read: %s", strerror(errno));
        }
        msg_len = PEEK_U32(buf);
@@ -239,6 +241,49 @@ mm_sshkey_sign(struct ssh *ssh, struct sshkey *key, u_char **sigp, size_t *lenp,
        return (0);
 }
 
+void
+mm_decode_activate_server_options(struct ssh *ssh, struct sshbuf *m)
+{
+       const u_char *p;
+       size_t len;
+       u_int i;
+       ServerOptions *newopts;
+       int r;
+
+       if ((r = sshbuf_get_string_direct(m, &p, &len)) != 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) 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
+
+       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]);
+       free(newopts);
+}
+
 #define GETPW(b, id) \
        do { \
                if ((r = sshbuf_get_string_direct(b, &p, &len)) != 0) \
@@ -254,8 +299,6 @@ mm_getpwnamallow(struct ssh *ssh, const char *username)
        struct sshbuf *m;
        struct passwd *pw;
        size_t len;
-       u_int i;
-       ServerOptions *newopts;
        int r;
        u_char ok;
        const u_char *p;
@@ -294,41 +337,10 @@ mm_getpwnamallow(struct ssh *ssh, const char *username)
 
 out:
        /* copy options block as a Match directive may have changed some */
-       if ((r = sshbuf_get_string_direct(m, &p, &len)) != 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) 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
-
-       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]);
-       process_permitopen(ssh, &options);
-       process_channel_timeouts(ssh, &options);
+       mm_decode_activate_server_options(ssh, m);
+       server_process_permitopen(ssh);
+       server_process_channel_timeouts(ssh);
        kex_set_server_sig_algs(ssh, options.pubkey_accepted_algos);
-       free(newopts);
        sshbuf_free(m);
 
        return (pw);
@@ -807,3 +819,91 @@ mm_ssh_gssapi_userok(char *user)
        return (authenticated);
 }
 #endif /* GSSAPI */
+
+/*
+ * Inform channels layer of permitopen options for a single forwarding
+ * direction (local/remote).
+ */
+static void
+server_process_permitopen_list(struct ssh *ssh, int listen,
+    char **opens, u_int num_opens)
+{
+       u_int i;
+       int port;
+       char *host, *arg, *oarg;
+       int where = listen ? FORWARD_REMOTE : FORWARD_LOCAL;
+       const char *what = listen ? "permitlisten" : "permitopen";
+
+       channel_clear_permission(ssh, FORWARD_ADM, where);
+       if (num_opens == 0)
+               return; /* permit any */
+
+       /* handle keywords: "any" / "none" */
+       if (num_opens == 1 && strcmp(opens[0], "any") == 0)
+               return;
+       if (num_opens == 1 && strcmp(opens[0], "none") == 0) {
+               channel_disable_admin(ssh, where);
+               return;
+       }
+       /* Otherwise treat it as a list of permitted host:port */
+       for (i = 0; i < num_opens; i++) {
+               oarg = arg = xstrdup(opens[i]);
+               host = hpdelim(&arg);
+               if (host == NULL)
+                       fatal_f("missing host in %s", what);
+               host = cleanhostname(host);
+               if (arg == NULL || ((port = permitopen_port(arg)) < 0))
+                       fatal_f("bad port number in %s", what);
+               /* Send it to channels layer */
+               channel_add_permission(ssh, FORWARD_ADM,
+                   where, host, port);
+               free(oarg);
+       }
+}
+
+/*
+ * Inform channels layer of permitopen options from configuration.
+ */
+void
+server_process_permitopen(struct ssh *ssh)
+{
+       server_process_permitopen_list(ssh, 0,
+           options.permitted_opens, options.num_permitted_opens);
+       server_process_permitopen_list(ssh, 1,
+           options.permitted_listens, options.num_permitted_listens);
+}
+
+void
+server_process_channel_timeouts(struct ssh *ssh)
+{
+       u_int i, secs;
+       char *type;
+
+       debug3_f("setting %u timeouts", options.num_channel_timeouts);
+       channel_clear_timeouts(ssh);
+       for (i = 0; i < options.num_channel_timeouts; i++) {
+               if (parse_pattern_interval(options.channel_timeouts[i],
+                   &type, &secs) != 0) {
+                       fatal_f("internal error: bad timeout %s",
+                           options.channel_timeouts[i]);
+               }
+               channel_add_timeout(ssh, type, secs);
+               free(type);
+       }
+}
+
+struct connection_info *
+server_get_connection_info(struct ssh *ssh, int populate, int use_dns)
+{
+       static struct connection_info ci;
+
+       if (ssh == NULL || !populate)
+               return &ci;
+       ci.host = use_dns ? ssh_remote_hostname(ssh) : ssh_remote_ipaddr(ssh);
+       ci.address = ssh_remote_ipaddr(ssh);
+       ci.laddress = ssh_local_ipaddr(ssh);
+       ci.lport = ssh_local_port(ssh);
+       ci.rdomain = ssh_packet_rdomain_in(ssh);
+       return &ci;
+}
+
index c020c2c..8dbd311 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor_wrap.h,v 1.49 2022/06/15 16:08:25 djm Exp $ */
+/* $OpenBSD: monitor_wrap.h,v 1.50 2024/05/17 00:30:24 djm Exp $ */
 
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
@@ -28,9 +28,6 @@
 #ifndef _MM_WRAP_H_
 #define _MM_WRAP_H_
 
-extern int use_privsep;
-#define PRIVSEP(x)     (use_privsep ? mm_##x : x)
-
 enum mm_keytype { MM_NOKEY, MM_HOSTKEY, MM_USERKEY };
 
 struct ssh;
@@ -61,6 +58,8 @@ int mm_hostbased_key_allowed(struct ssh *, struct passwd *, const char *,
 int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
     const u_char *, size_t, const char *, u_int, struct sshkey_sig_details **);
 
+void mm_decode_activate_server_options(struct ssh *ssh, struct sshbuf *m);
+
 #ifdef GSSAPI
 OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID);
 OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *,
@@ -84,4 +83,10 @@ void mm_send_keystate(struct ssh *, struct monitor*);
 int mm_bsdauth_query(void *, char **, char **, u_int *, char ***, u_int **);
 int mm_bsdauth_respond(void *, u_int, char **);
 
+/* config / channels glue */
+void    server_process_permitopen(struct ssh *);
+void    server_process_channel_timeouts(struct ssh *ssh);
+struct connection_info *
+        server_get_connection_info(struct ssh *, int, int);
+
 #endif /* _MM_WRAP_H_ */
index 907f233..3aa2d5d 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: msg.c,v 1.20 2020/10/18 11:32:01 djm Exp $ */
+/* $OpenBSD: msg.c,v 1.21 2024/05/17 00:30:24 djm Exp $ */
 /*
  * Copyright (c) 2002 Markus Friedl.  All rights reserved.
  *
@@ -45,7 +45,7 @@ ssh_msg_send(int fd, u_char type, struct sshbuf *m)
        u_char buf[5];
        u_int mlen = sshbuf_len(m);
 
-       debug3_f("type %u", (unsigned int)type & 0xff);
+       debug3_f("type %u len %zu", (unsigned int)type & 0xff, sshbuf_len(m));
 
        put_u32(buf, mlen + 1);
        buf[4] = type;          /* 1st byte of payload is mesg-type */
@@ -57,6 +57,7 @@ ssh_msg_send(int fd, u_char type, struct sshbuf *m)
                error_f("write: %s", strerror(errno));
                return (-1);
        }
+       debug3_f("done");
        return (0);
 }
 
index 4ac450d..33e821c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.c,v 1.313 2023/12/18 14:45:17 djm Exp $ */
+/* $OpenBSD: packet.c,v 1.314 2024/05/17 00:30:24 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -514,6 +514,94 @@ ssh_remote_ipaddr(struct ssh *ssh)
        return ssh->remote_ipaddr;
 }
 
+/*
+ * Returns the remote DNS hostname as a string. The returned string must not
+ * be freed. NB. this will usually trigger a DNS query. Return value is on
+ * heap and no caching is performed.
+ * This function does additional checks on the hostname to mitigate some
+ * attacks on based on conflation of hostnames and addresses and will
+ * fall back to returning an address on error.
+ */
+
+char *
+ssh_remote_hostname(struct ssh *ssh)
+{
+       struct sockaddr_storage from;
+       socklen_t fromlen;
+       struct addrinfo hints, *ai, *aitop;
+       char name[NI_MAXHOST], ntop2[NI_MAXHOST];
+       const char *ntop = ssh_remote_ipaddr(ssh);
+
+       /* Get IP address of client. */
+       fromlen = sizeof(from);
+       memset(&from, 0, sizeof(from));
+       if (getpeername(ssh_packet_get_connection_in(ssh),
+           (struct sockaddr *)&from, &fromlen) == -1) {
+               debug_f("getpeername failed: %.100s", strerror(errno));
+               return xstrdup(ntop);
+       }
+
+       debug3_f("trying to reverse map address %.100s.", ntop);
+       /* Map the IP address to a host name. */
+       if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
+           NULL, 0, NI_NAMEREQD) != 0) {
+               /* Host name not found.  Use ip address. */
+               return xstrdup(ntop);
+       }
+
+       /*
+        * if reverse lookup result looks like a numeric hostname,
+        * someone is trying to trick us by PTR record like following:
+        *      1.1.1.10.in-addr.arpa.  IN PTR  2.3.4.5
+        */
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+       hints.ai_flags = AI_NUMERICHOST;
+       if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
+               logit("Nasty PTR record \"%s\" is set up for %s, ignoring",
+                   name, ntop);
+               freeaddrinfo(ai);
+               return xstrdup(ntop);
+       }
+
+       /* Names are stored in lowercase. */
+       lowercase(name);
+
+       /*
+        * Map it back to an IP address and check that the given
+        * address actually is an address of this host.  This is
+        * necessary because anyone with access to a name server can
+        * define arbitrary names for an IP address. Mapping from
+        * name to IP address can be trusted better (but can still be
+        * fooled if the intruder has access to the name server of
+        * the domain).
+        */
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = from.ss_family;
+       hints.ai_socktype = SOCK_STREAM;
+       if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
+               logit("reverse mapping checking getaddrinfo for %.700s "
+                   "[%s] failed.", name, ntop);
+               return xstrdup(ntop);
+       }
+       /* Look for the address from the list of addresses. */
+       for (ai = aitop; ai; ai = ai->ai_next) {
+               if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
+                   sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
+                   (strcmp(ntop, ntop2) == 0))
+                               break;
+       }
+       freeaddrinfo(aitop);
+       /* If we reached the end of the list, the address was not there. */
+       if (ai == NULL) {
+               /* Address not found for the host name. */
+               logit("Address %.100s maps to %.600s, but this does not "
+                   "map back to the address.", ntop, name);
+               return xstrdup(ntop);
+       }
+       return xstrdup(name);
+}
+
 /* Returns the port number of the remote host. */
 
 int
index 7dbe682..d3a4c8a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.h,v 1.96 2023/12/18 14:45:17 djm Exp $ */
+/* $OpenBSD: packet.h,v 1.97 2024/05/17 00:30:24 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -159,6 +159,7 @@ int  ssh_remote_port(struct ssh *);
 const char *ssh_local_ipaddr(struct ssh *);
 int     ssh_local_port(struct ssh *);
 const char *ssh_packet_rdomain_in(struct ssh *);
+char   *ssh_remote_hostname(struct ssh *);
 
 void    ssh_packet_set_rekey_limits(struct ssh *, u_int64_t, u_int32_t);
 time_t  ssh_packet_get_rekey_timeout(struct ssh *);
index 52d7471..0b17050 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: pathnames.h,v 1.31 2019/11/12 19:33:08 markus Exp $ */
+/* $OpenBSD: pathnames.h,v 1.32 2024/05/17 00:30:24 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -39,6 +39,9 @@
 
 #define _PATH_SSH_PROGRAM              "/usr/bin/ssh"
 
+/* Binary paths for the sshd components */
+#define _PATH_SSHD_SESSION             "/usr/libexec/sshd-session"
+
 /*
  * The process id of the daemon listening for connections is saved here to
  * make it easier to kill the correct daemon when necessary.
index 85fb86d..b8c64bf 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.c,v 1.405 2024/03/04 02:16:11 djm Exp $ */
+/* $OpenBSD: servconf.c,v 1.406 2024/05/17 00:30:24 djm Exp $ */
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -64,8 +64,6 @@ static void parse_server_config_depth(ServerOptions *options,
     const char *filename, struct sshbuf *conf, struct include_list *includes,
     struct connection_info *connectinfo, int flags, int *activep, int depth);
 
-/* Use of privilege separation or not */
-extern int use_privsep;
 extern struct sshbuf *cfg;
 
 /* Initializes the server options to their default values. */
@@ -179,6 +177,7 @@ initialize_server_options(ServerOptions *options)
        options->channel_timeouts = NULL;
        options->num_channel_timeouts = 0;
        options->unused_connection_timeout = -1;
+       options->sshd_session_path = NULL;
 }
 
 /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
@@ -422,13 +421,11 @@ fill_default_server_options(ServerOptions *options)
                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);
 
        assemble_algorithms(options);
 
-       /* Turn privilege separation and sandboxing on by default */
-       if (use_privsep == -1)
-               use_privsep = PRIVSEP_ON;
-
 #define CLEAR_ON_NONE(v) \
        do { \
                if (option_clear_or_none(v)) { \
@@ -503,6 +500,7 @@ typedef enum {
        sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
        sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
        sRequiredRSASize, sChannelTimeout, sUnusedConnectionTimeout,
+       sSshdMonitorPath,
        sDeprecated, sIgnore, sUnsupported
 } ServerOpCodes;
 
@@ -649,6 +647,7 @@ static struct {
        { "requiredrsasize", sRequiredRSASize, SSHCFG_ALL },
        { "channeltimeout", sChannelTimeout, SSHCFG_ALL },
        { "unusedconnectiontimeout", sUnusedConnectionTimeout, SSHCFG_ALL },
+       { "sshdmonitorpath", sSshdMonitorPath, SSHCFG_GLOBAL },
        { NULL, sBadOption, 0 }
 };
 
@@ -851,95 +850,6 @@ process_queued_listen_addrs(ServerOptions *options)
        options->num_queued_listens = 0;
 }
 
-/*
- * Inform channels layer of permitopen options for a single forwarding
- * direction (local/remote).
- */
-static void
-process_permitopen_list(struct ssh *ssh, ServerOpCodes opcode,
-    char **opens, u_int num_opens)
-{
-       u_int i;
-       int port;
-       char *host, *arg, *oarg;
-       int where = opcode == sPermitOpen ? FORWARD_LOCAL : FORWARD_REMOTE;
-       const char *what = lookup_opcode_name(opcode);
-
-       channel_clear_permission(ssh, FORWARD_ADM, where);
-       if (num_opens == 0)
-               return; /* permit any */
-
-       /* handle keywords: "any" / "none" */
-       if (num_opens == 1 && strcmp(opens[0], "any") == 0)
-               return;
-       if (num_opens == 1 && strcmp(opens[0], "none") == 0) {
-               channel_disable_admin(ssh, where);
-               return;
-       }
-       /* Otherwise treat it as a list of permitted host:port */
-       for (i = 0; i < num_opens; i++) {
-               oarg = arg = xstrdup(opens[i]);
-               host = hpdelim(&arg);
-               if (host == NULL)
-                       fatal_f("missing host in %s", what);
-               host = cleanhostname(host);
-               if (arg == NULL || ((port = permitopen_port(arg)) < 0))
-                       fatal_f("bad port number in %s", what);
-               /* Send it to channels layer */
-               channel_add_permission(ssh, FORWARD_ADM,
-                   where, host, port);
-               free(oarg);
-       }
-}
-
-/*
- * Inform channels layer of permitopen options from configuration.
- */
-void
-process_permitopen(struct ssh *ssh, ServerOptions *options)
-{
-       process_permitopen_list(ssh, sPermitOpen,
-           options->permitted_opens, options->num_permitted_opens);
-       process_permitopen_list(ssh, sPermitListen,
-           options->permitted_listens,
-           options->num_permitted_listens);
-}
-
-void
-process_channel_timeouts(struct ssh *ssh, ServerOptions *options)
-{
-       int secs;
-       u_int i;
-       char *type;
-
-       debug3_f("setting %u timeouts", options->num_channel_timeouts);
-       channel_clear_timeouts(ssh);
-       for (i = 0; i < options->num_channel_timeouts; i++) {
-               if (parse_pattern_interval(options->channel_timeouts[i],
-                   &type, &secs) != 0) {
-                       fatal_f("internal error: bad timeout %s",
-                           options->channel_timeouts[i]);
-               }
-               channel_add_timeout(ssh, type, secs);
-               free(type);
-       }
-}
-
-struct connection_info *
-get_connection_info(struct ssh *ssh, int populate, int use_dns)
-{
-       static struct connection_info ci;
-
-       if (ssh == NULL || !populate)
-               return &ci;
-       ci.host = auth_get_canonical_hostname(ssh, use_dns);
-       ci.address = ssh_remote_ipaddr(ssh);
-       ci.laddress = ssh_local_ipaddr(ssh);
-       ci.lport = ssh_local_port(ssh);
-       ci.rdomain = ssh_packet_rdomain_in(ssh);
-       return &ci;
-}
-
 /*
  * The strategy for the Match blocks is that the config file is parsed twice.
  *
@@ -2532,6 +2442,10 @@ process_server_config_line_depth(ServerOptions *options, char *line,
                }
                goto parse_time;
 
+       case sSshdMonitorPath:
+               charptr = &options->sshd_session_path;
+               goto parse_filename;
+
        case sDeprecated:
        case sIgnore:
        case sUnsupported:
@@ -3097,6 +3011,7 @@ dump_config(ServerOptions *o)
        dump_cfg_string(sHostKeyAlgorithms, o->hostkeyalgorithms);
        dump_cfg_string(sPubkeyAcceptedAlgorithms, o->pubkey_accepted_algos);
        dump_cfg_string(sRDomain, o->routing_domain);
+       dump_cfg_string(sSshdMonitorPath, o->sshd_session_path);
 
        /* string arguments requiring a lookup */
        dump_cfg_string(sLogLevel, log_level_name(o->log_level));
index bff6f26..19d6acb 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: servconf.h,v 1.160 2023/09/06 23:35:35 djm Exp $ */
+/* $OpenBSD: servconf.h,v 1.161 2024/05/17 00:30:24 djm Exp $ */
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
 #define        PERMIT_NO_PASSWD        2
 #define        PERMIT_YES              3
 
-/* use_privsep */
-#define PRIVSEP_OFF            0
-#define PRIVSEP_ON             1
-#define PRIVSEP_NOSANDBOX      2
-
 /* PermitOpen */
 #define PERMITOPEN_ANY         0
 #define PERMITOPEN_NONE                -2
@@ -231,6 +226,8 @@ typedef struct {
        u_int   num_channel_timeouts;
 
        int     unused_connection_timeout;
+
+       char   *sshd_session_path;
 }       ServerOptions;
 
 /* Information about the incoming connection as used by Match */
@@ -295,18 +292,16 @@ TAILQ_HEAD(include_list, include_item);
                M_CP_STRARRAYOPT(subsystem_args, num_subsystems); \
        } while (0)
 
-struct connection_info *get_connection_info(struct ssh *, int, int);
 void    initialize_server_options(ServerOptions *);
 void    fill_default_server_options(ServerOptions *);
 int     process_server_config_line(ServerOptions *, char *, const char *, int,
            int *, struct connection_info *, struct include_list *includes);
-void    process_permitopen(struct ssh *ssh, ServerOptions *options);
-void    process_channel_timeouts(struct ssh *ssh, ServerOptions *);
 void    load_server_config(const char *, struct sshbuf *);
 void    parse_server_config(ServerOptions *, const char *, struct sshbuf *,
            struct include_list *includes, struct connection_info *, int);
 void    parse_server_match_config(ServerOptions *,
            struct include_list *includes, struct connection_info *);
+int     parse_channel_timeout(const char *, char **, u_int *);
 int     parse_server_match_testspec(struct connection_info *, char *);
 int     server_match_spec_complete(struct connection_info *);
 void    servconf_merge_subsystems(ServerOptions *, ServerOptions *);
index 2e3101e..689d21e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: serverloop.c,v 1.238 2024/04/30 02:14:10 djm Exp $ */
+/* $OpenBSD: serverloop.c,v 1.239 2024/05/17 00:30:24 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -80,44 +80,23 @@ extern ServerOptions options;
 /* XXX */
 extern Authctxt *the_authctxt;
 extern struct sshauthopt *auth_opts;
-extern int use_privsep;
 
 static int no_more_sessions = 0; /* Disallow further sessions. */
 
 static volatile sig_atomic_t child_terminated = 0;     /* The child has terminated. */
 
-/* Cleanup on signals (!use_privsep case only) */
-static volatile sig_atomic_t received_sigterm = 0;
-
 /* prototypes */
 static void server_init_dispatch(struct ssh *);
 
 /* requested tunnel forwarding interface(s), shared with session.c */
 char *tun_fwd_ifnames = NULL;
 
-/* returns 1 if bind to specified port by specified user is permitted */
-static int
-bind_permitted(int port, uid_t uid)
-{
-       if (use_privsep)
-               return 1; /* allow system to decide */
-       if (port < IPPORT_RESERVED && uid != 0)
-               return 0;
-       return 1;
-}
-
 static void
 sigchld_handler(int sig)
 {
        child_terminated = 1;
 }
 
-static void
-sigterm_handler(int sig)
-{
-       received_sigterm = sig;
-}
-
 static void
 client_alive_check(struct ssh *ssh)
 {
@@ -348,12 +327,6 @@ server_loop2(struct ssh *ssh, Authctxt *authctxt)
        connection_in = ssh_packet_get_connection_in(ssh);
        connection_out = ssh_packet_get_connection_out(ssh);
 
-       if (!use_privsep) {
-               ssh_signal(SIGTERM, sigterm_handler);
-               ssh_signal(SIGINT, sigterm_handler);
-               ssh_signal(SIGQUIT, sigterm_handler);
-       }
-
        server_init_dispatch(ssh);
 
        for (;;) {
@@ -377,12 +350,6 @@ server_loop2(struct ssh *ssh, Authctxt *authctxt)
                if (sigprocmask(SIG_SETMASK, &osigset, NULL) == -1)
                        error_f("osigset sigprocmask: %s", strerror(errno));
 
-               if (received_sigterm) {
-                       logit("Exiting on signal %d", (int)received_sigterm);
-                       /* Clean up sessions, utmp, etc. */
-                       cleanup_exit(255);
-               }
-
                channel_after_poll(ssh, pfd, npfd_active);
                if (conn_in_ready &&
                    process_input(ssh, connection_in) < 0)
@@ -492,7 +459,7 @@ server_request_direct_streamlocal(struct ssh *ssh)
        /* XXX fine grained permissions */
        if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 &&
            auth_opts->permit_port_forwarding_flag &&
-           !options.disable_forwarding && (pw->pw_uid == 0 || use_privsep)) {
+           !options.disable_forwarding) {
                c = channel_connect_to_path(ssh, target,
                    "direct-streamlocal@openssh.com", "direct-streamlocal");
        } else {
@@ -781,9 +748,7 @@ server_input_global_request(int type, u_int32_t seq, struct ssh *ssh)
                    (options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 ||
                    !auth_opts->permit_port_forwarding_flag ||
                    options.disable_forwarding ||
-                   (!want_reply && fwd.listen_port == 0) ||
-                   (fwd.listen_port != 0 &&
-                   !bind_permitted(fwd.listen_port, pw->pw_uid))) {
+                   (!want_reply && fwd.listen_port == 0)) {
                        success = 0;
                        ssh_packet_send_debug(ssh, "Server has disabled port forwarding.");
                } else {
@@ -816,8 +781,7 @@ server_input_global_request(int type, u_int32_t seq, struct ssh *ssh)
                /* check permissions */
                if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0
                    || !auth_opts->permit_port_forwarding_flag ||
-                   options.disable_forwarding ||
-                   (pw->pw_uid != 0 && !use_privsep)) {
+                   options.disable_forwarding) {
                        success = 0;
                        ssh_packet_send_debug(ssh, "Server has disabled "
                            "streamlocal forwarding.");
index 88ebe22..e0239a2 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.337 2024/02/01 02:37:33 djm Exp $ */
+/* $OpenBSD: session.c,v 1.338 2024/05/17 00:30:24 djm Exp $ */
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -705,8 +705,6 @@ do_login(struct ssh *ssh, Session *s, const char *command)
 {
        socklen_t fromlen;
        struct sockaddr_storage from;
-       struct passwd * pw = s->pw;
-       pid_t pid = getpid();
 
        /*
         * Get IP address of client. If the connection is not a socket, let
@@ -722,13 +720,6 @@ do_login(struct ssh *ssh, Session *s, const char *command)
                }
        }
 
-       /* Record that there was a login on that tty from the remote host. */
-       if (!use_privsep)
-               record_login(pid, s->tty, pw->pw_name, pw->pw_uid,
-                   session_get_remote_name_or_ip(ssh, utmp_len,
-                   options.use_dns),
-                   (struct sockaddr *)&from, fromlen);
-
        if (check_quietlogin(s, command))
                return;
 
@@ -1606,8 +1597,7 @@ session_pty_req(struct ssh *ssh, Session *s)
 
        /* Allocate a pty and open it. */
        debug("Allocating pty.");
-       if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
-           sizeof(s->tty)))) {
+       if (!mm_pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) {
                free(s->term);
                s->term = NULL;
                s->ptyfd = -1;
@@ -1622,9 +1612,6 @@ session_pty_req(struct ssh *ssh, Session *s)
        if ((r = sshpkt_get_end(ssh)) != 0)
                sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
 
-       if (!use_privsep)
-               pty_setowner(s->pw, s->tty);
-
        /* Set window size from the packet. */
        pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
 
@@ -1840,7 +1827,7 @@ session_signal_req(struct ssh *ssh, Session *s)
                    signame, s->forced ? "forced-command" : "subsystem");
                goto out;
        }
-       if (!use_privsep || mm_is_monitor()) {
+       if (mm_is_monitor()) {
                error_f("session signalling requires privilege separation");
                goto out;
        }
@@ -1983,7 +1970,7 @@ session_pty_cleanup2(Session *s)
 void
 session_pty_cleanup(Session *s)
 {
-       PRIVSEP(session_pty_cleanup2(s));
+       mm_session_pty_cleanup2(s);
 }
 
 static char *
@@ -2354,7 +2341,7 @@ do_cleanup(struct ssh *ssh, Authctxt *authctxt)
         * Cleanup ptys/utmp only if privsep is disabled,
         * or if running in monitor.
         */
-       if (!use_privsep || mm_is_monitor())
+       if (mm_is_monitor())
                session_destroy_all(ssh, session_pty_cleanup2);
 }
 
index 54f82cf..e496403 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh_api.c,v 1.28 2024/01/09 21:39:14 djm Exp $ */
+/* $OpenBSD: ssh_api.c,v 1.29 2024/05/17 00:30:24 djm Exp $ */
 /*
  * Copyright (c) 2012 Markus Friedl.  All rights reserved.
  *
@@ -25,6 +25,7 @@
 #include "log.h"
 #include "authfile.h"
 #include "sshkey.h"
+#include "dh.h"
 #include "misc.h"
 #include "ssh2.h"
 #include "version.h"
@@ -45,10 +46,8 @@ int  _ssh_host_key_sign(struct ssh *, struct sshkey *, struct sshkey *,
     u_char **, size_t *, const u_char *, size_t, const char *);
 
 /*
- * stubs for the server side implementation of kex.
- * disable privsep so our stubs will never be called.
+ * stubs for privsep calls in the server side implementation of kex.
  */
-int    use_privsep = 0;
 int    mm_sshkey_sign(struct sshkey *, u_char **, u_int *,
     const u_char *, u_int, const char *, const char *, const char *, u_int);
 
@@ -61,14 +60,20 @@ mm_sshkey_sign(struct sshkey *key, u_char **sigp, u_int *lenp,
     const u_char *data, u_int datalen, const char *alg,
     const char *sk_provider, const char *sk_pin, u_int compat)
 {
-       return (-1);
+       size_t slen = 0;
+       int ret;
+
+       ret = sshkey_sign(key, sigp, &slen, data, datalen, alg,
+           sk_provider, sk_pin, compat);
+       *lenp = slen;
+       return ret;
 }
 
 #ifdef WITH_OPENSSL
 DH *
 mm_choose_dh(int min, int nbits, int max)
 {
-       return (NULL);
+       return choose_dh(min, nbits, max);
 }
 #endif
 
diff --git a/usr.bin/ssh/sshd-session.c b/usr.bin/ssh/sshd-session.c
new file mode 100644 (file)
index 0000000..820e3cb
--- /dev/null
@@ -0,0 +1,1354 @@
+/* $OpenBSD: sshd-session.c,v 1.1 2024/05/17 00:30:24 djm Exp $ */
+/*
+ * SSH2 implementation:
+ * Privilege Separation:
+ *
+ * Copyright (c) 2000, 2001, 2002 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2002 Niels Provos.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/tree.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <limits.h>
+
+#ifdef WITH_OPENSSL
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#endif
+
+#include "xmalloc.h"
+#include "ssh.h"
+#include "ssh2.h"
+#include "sshpty.h"
+#include "packet.h"
+#include "log.h"
+#include "sshbuf.h"
+#include "misc.h"
+#include "match.h"
+#include "servconf.h"
+#include "uidswap.h"
+#include "compat.h"
+#include "cipher.h"
+#include "digest.h"
+#include "sshkey.h"
+#include "kex.h"
+#include "authfile.h"
+#include "pathnames.h"
+#include "atomicio.h"
+#include "canohost.h"
+#include "hostfile.h"
+#include "auth.h"
+#include "authfd.h"
+#include "msg.h"
+#include "dispatch.h"
+#include "channels.h"
+#include "session.h"
+#include "monitor.h"
+#ifdef GSSAPI
+#include "ssh-gss.h"
+#endif
+#include "monitor_wrap.h"
+#include "ssh-sandbox.h"
+#include "auth-options.h"
+#include "version.h"
+#include "ssherr.h"
+#include "sk-api.h"
+#include "srclimit.h"
+#include "dh.h"
+
+/* Re-exec fds */
+#define REEXEC_DEVCRYPTO_RESERVED_FD   (STDERR_FILENO + 1)
+#define REEXEC_STARTUP_PIPE_FD         (STDERR_FILENO + 2)
+#define REEXEC_CONFIG_PASS_FD          (STDERR_FILENO + 3)
+#define REEXEC_MIN_FREE_FD             (STDERR_FILENO + 4)
+
+extern char *__progname;
+
+/* Server configuration options. */
+ServerOptions options;
+
+/* Name of the server configuration file. */
+char *config_file_name = _PATH_SERVER_CONFIG_FILE;
+
+/*
+ * Debug mode flag.  This can be set on the command line.  If debug
+ * mode is enabled, extra debugging output will be sent to the system
+ * log, the daemon will not go to background, and will exit after processing
+ * the first connection.
+ */
+int debug_flag = 0;
+
+/* Flag indicating that the daemon is being started from inetd. */
+static int inetd_flag = 0;
+
+/* debug goes to stderr unless inetd_flag is set */
+static int log_stderr = 0;
+
+/* Saved arguments to main(). */
+static char **saved_argv;
+
+/* Daemon's agent connection */
+int auth_sock = -1;
+static int have_agent = 0;
+
+/*
+ * Any really sensitive data in the application is contained in this
+ * structure. The idea is that this structure could be locked into memory so
+ * that the pages do not get written into swap.  However, there are some
+ * problems. The private key contains BIGNUMs, and we do not (in principle)
+ * have access to the internals of them, and locking just the structure is
+ * not very useful.  Currently, memory locking is not implemented.
+ */
+struct {
+       u_int           num_hostkeys;
+       struct sshkey   **host_keys;            /* all private host keys */
+       struct sshkey   **host_pubkeys;         /* all public host keys */
+       struct sshkey   **host_certificates;    /* all public host certificates */
+} sensitive_data;
+
+/* record remote hostname or ip */
+u_int utmp_len = HOST_NAME_MAX+1;
+
+static int startup_pipe = -1;          /* in child */
+
+/* variables used for privilege separation */
+struct monitor *pmonitor = NULL;
+int privsep_is_preauth = 1;
+
+/* global connection state and authentication contexts */
+Authctxt *the_authctxt = NULL;
+struct ssh *the_active_state;
+
+/* global key/cert auth options. XXX move to permanent ssh->authctxt? */
+struct sshauthopt *auth_opts = NULL;
+
+/* sshd_config buffer */
+struct sshbuf *cfg;
+
+/* Included files from the configuration file */
+struct include_list includes = TAILQ_HEAD_INITIALIZER(includes);
+
+/* message to be displayed after login */
+struct sshbuf *loginmsg;
+
+/* Prototypes for various functions defined later in this file. */
+void destroy_sensitive_data(void);
+void demote_sensitive_data(void);
+static void do_ssh2_kex(struct ssh *);
+
+/*
+ * Signal handler for the alarm after the login grace period has expired.
+ */
+static void
+grace_alarm_handler(int sig)
+{
+       /*
+        * Try to kill any processes that we have spawned, E.g. authorized
+        * keys command helpers or privsep children.
+        */
+       if (getpgid(0) == getpid()) {
+               ssh_signal(SIGTERM, SIG_IGN);
+               kill(0, SIGTERM);
+       }
+
+       /* Log error and exit. */
+       sigdie("Timeout before authentication for %s port %d",
+           ssh_remote_ipaddr(the_active_state),
+           ssh_remote_port(the_active_state));
+}
+
+/* Destroy the host and server keys.  They will no longer be needed. */
+void
+destroy_sensitive_data(void)
+{
+       u_int i;
+
+       for (i = 0; i < options.num_host_key_files; i++) {
+               if (sensitive_data.host_keys[i]) {
+                       sshkey_free(sensitive_data.host_keys[i]);
+                       sensitive_data.host_keys[i] = NULL;
+               }
+               if (sensitive_data.host_certificates[i]) {
+                       sshkey_free(sensitive_data.host_certificates[i]);
+                       sensitive_data.host_certificates[i] = NULL;
+               }
+       }
+}
+
+/* Demote private to public keys for network child */
+void
+demote_sensitive_data(void)
+{
+       struct sshkey *tmp;
+       u_int i;
+       int r;
+
+       for (i = 0; i < options.num_host_key_files; i++) {
+               if (sensitive_data.host_keys[i]) {
+                       if ((r = sshkey_from_private(
+                           sensitive_data.host_keys[i], &tmp)) != 0)
+                               fatal_r(r, "could not demote host %s key",
+                                   sshkey_type(sensitive_data.host_keys[i]));
+                       sshkey_free(sensitive_data.host_keys[i]);
+                       sensitive_data.host_keys[i] = tmp;
+               }
+               /* Certs do not need demotion */
+       }
+}
+
+static void
+privsep_preauth_child(void)
+{
+       gid_t gidset[1];
+       struct passwd *pw;
+
+       /* Enable challenge-response authentication for privilege separation */
+       privsep_challenge_enable();
+
+#ifdef GSSAPI
+       /* Cache supported mechanism OIDs for later use */
+       ssh_gssapi_prepare_supported_oids();
+#endif
+
+       /* Demote the private keys to public keys. */
+       demote_sensitive_data();
+
+       /* Demote the child */
+       if (getuid() == 0 || geteuid() == 0) {
+               if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL)
+                       fatal("Privilege separation user %s does not exist",
+                           SSH_PRIVSEP_USER);
+               pw = pwcopy(pw); /* Ensure mutable */
+               endpwent();
+               freezero(pw->pw_passwd, strlen(pw->pw_passwd));
+
+               /* Change our root directory */
+               if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1)
+                       fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR,
+                           strerror(errno));
+               if (chdir("/") == -1)
+                       fatal("chdir(\"/\"): %s", strerror(errno));
+
+               /*
+                * Drop our privileges
+                * NB. Can't use setusercontext() after chroot.
+                */
+               debug3("privsep user:group %u:%u", (u_int)pw->pw_uid,
+                   (u_int)pw->pw_gid);
+               gidset[0] = pw->pw_gid;
+               if (setgroups(1, gidset) == -1)
+                       fatal("setgroups: %.100s", strerror(errno));
+               permanently_set_uid(pw);
+       }
+}
+
+static int
+privsep_preauth(struct ssh *ssh)
+{
+       int status, r;
+       pid_t pid;
+       struct ssh_sandbox *box = NULL;
+
+       /* Set up unprivileged child process to deal with network data */
+       pmonitor = monitor_init();
+       /* Store a pointer to the kex for later rekeying */
+       pmonitor->m_pkex = &ssh->kex;
+
+       box = ssh_sandbox_init();
+       pid = fork();
+       if (pid == -1) {
+               fatal("fork of unprivileged child failed");
+       } else if (pid != 0) {
+               debug2("Network child is on pid %ld", (long)pid);
+
+               pmonitor->m_pid = pid;
+               if (have_agent) {
+                       r = ssh_get_authentication_socket(&auth_sock);
+                       if (r != 0) {
+                               error_r(r, "Could not get agent socket");
+                               have_agent = 0;
+                       }
+               }
+               if (box != NULL)
+                       ssh_sandbox_parent_preauth(box, pid);
+               monitor_child_preauth(ssh, pmonitor);
+
+               /* Wait for the child's exit status */
+               while (waitpid(pid, &status, 0) == -1) {
+                       if (errno == EINTR)
+                               continue;
+                       pmonitor->m_pid = -1;
+                       fatal_f("waitpid: %s", strerror(errno));
+               }
+               privsep_is_preauth = 0;
+               pmonitor->m_pid = -1;
+               if (WIFEXITED(status)) {
+                       if (WEXITSTATUS(status) != 0)
+                               fatal_f("preauth child exited with status %d",
+                                   WEXITSTATUS(status));
+               } else if (WIFSIGNALED(status))
+                       fatal_f("preauth child terminated by signal %d",
+                           WTERMSIG(status));
+               if (box != NULL)
+                       ssh_sandbox_parent_finish(box);
+               return 1;
+       } else {
+               /* child */
+               close(pmonitor->m_sendfd);
+               close(pmonitor->m_log_recvfd);
+
+               /* Arrange for logging to be sent to the monitor */
+               set_log_handler(mm_log_handler, pmonitor);
+
+               privsep_preauth_child();
+               setproctitle("%s", "[net]");
+               if (box != NULL)
+                       ssh_sandbox_child(box);
+
+               return 0;
+       }
+}
+
+static void
+privsep_postauth(struct ssh *ssh, Authctxt *authctxt)
+{
+       /* New socket pair */
+       monitor_reinit(pmonitor);
+
+       pmonitor->m_pid = fork();
+       if (pmonitor->m_pid == -1)
+               fatal("fork of unprivileged child failed");
+       else if (pmonitor->m_pid != 0) {
+               verbose("User child is on pid %ld", (long)pmonitor->m_pid);
+               sshbuf_reset(loginmsg);
+               monitor_clear_keystate(ssh, pmonitor);
+               monitor_child_postauth(ssh, pmonitor);
+
+               /* NEVERREACHED */
+               exit(0);
+       }
+
+       /* child */
+
+       close(pmonitor->m_sendfd);
+       pmonitor->m_sendfd = -1;
+
+       /* Demote the private keys to public keys. */
+       demote_sensitive_data();
+
+       /* Drop privileges */
+       do_setusercontext(authctxt->pw);
+
+       /* It is safe now to apply the key state */
+       monitor_apply_keystate(ssh, pmonitor);
+
+       /*
+        * Tell the packet layer that authentication was successful, since
+        * this information is not part of the key state.
+        */
+       ssh_packet_set_authenticated(ssh);
+}
+
+static void
+append_hostkey_type(struct sshbuf *b, const char *s)
+{
+       int r;
+
+       if (match_pattern_list(s, options.hostkeyalgorithms, 0) != 1) {
+               debug3_f("%s key not permitted by HostkeyAlgorithms", s);
+               return;
+       }
+       if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) > 0 ? "," : "", s)) != 0)
+               fatal_fr(r, "sshbuf_putf");
+}
+
+static char *
+list_hostkey_types(void)
+{
+       struct sshbuf *b;
+       struct sshkey *key;
+       char *ret;
+       u_int i;
+
+       if ((b = sshbuf_new()) == NULL)
+               fatal_f("sshbuf_new failed");
+       for (i = 0; i < options.num_host_key_files; i++) {
+               key = sensitive_data.host_keys[i];
+               if (key == NULL)
+                       key = sensitive_data.host_pubkeys[i];
+               if (key == NULL)
+                       continue;
+               switch (key->type) {
+               case KEY_RSA:
+                       /* for RSA we also support SHA2 signatures */
+                       append_hostkey_type(b, "rsa-sha2-512");
+                       append_hostkey_type(b, "rsa-sha2-256");
+                       /* FALLTHROUGH */
+               case KEY_DSA:
+               case KEY_ECDSA:
+               case KEY_ED25519:
+               case KEY_ECDSA_SK:
+               case KEY_ED25519_SK:
+               case KEY_XMSS:
+                       append_hostkey_type(b, sshkey_ssh_name(key));
+                       break;
+               }
+               /* If the private key has a cert peer, then list that too */
+               key = sensitive_data.host_certificates[i];
+               if (key == NULL)
+                       continue;
+               switch (key->type) {
+               case KEY_RSA_CERT:
+                       /* for RSA we also support SHA2 signatures */
+                       append_hostkey_type(b,
+                           "rsa-sha2-512-cert-v01@openssh.com");
+                       append_hostkey_type(b,
+                           "rsa-sha2-256-cert-v01@openssh.com");
+                       /* FALLTHROUGH */
+               case KEY_DSA_CERT:
+               case KEY_ECDSA_CERT:
+               case KEY_ED25519_CERT:
+               case KEY_ECDSA_SK_CERT:
+               case KEY_ED25519_SK_CERT:
+               case KEY_XMSS_CERT:
+                       append_hostkey_type(b, sshkey_ssh_name(key));
+                       break;
+               }
+       }
+       if ((ret = sshbuf_dup_string(b)) == NULL)
+               fatal_f("sshbuf_dup_string failed");
+       sshbuf_free(b);
+       debug_f("%s", ret);
+       return ret;
+}
+
+static struct sshkey *
+get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
+{
+       u_int i;
+       struct sshkey *key;
+
+       for (i = 0; i < options.num_host_key_files; i++) {
+               switch (type) {
+               case KEY_RSA_CERT:
+               case KEY_DSA_CERT:
+               case KEY_ECDSA_CERT:
+               case KEY_ED25519_CERT:
+               case KEY_ECDSA_SK_CERT:
+               case KEY_ED25519_SK_CERT:
+               case KEY_XMSS_CERT:
+                       key = sensitive_data.host_certificates[i];
+                       break;
+               default:
+                       key = sensitive_data.host_keys[i];
+                       if (key == NULL && !need_private)
+                               key = sensitive_data.host_pubkeys[i];
+                       break;
+               }
+               if (key == NULL || key->type != type)
+                       continue;
+               switch (type) {
+               case KEY_ECDSA:
+               case KEY_ECDSA_SK:
+               case KEY_ECDSA_CERT:
+               case KEY_ECDSA_SK_CERT:
+                       if (key->ecdsa_nid != nid)
+                               continue;
+                       /* FALLTHROUGH */
+               default:
+                       return need_private ?
+                           sensitive_data.host_keys[i] : key;
+               }
+       }
+       return NULL;
+}
+
+struct sshkey *
+get_hostkey_public_by_type(int type, int nid, struct ssh *ssh)
+{
+       return get_hostkey_by_type(type, nid, 0, ssh);
+}
+
+struct sshkey *
+get_hostkey_private_by_type(int type, int nid, struct ssh *ssh)
+{
+       return get_hostkey_by_type(type, nid, 1, ssh);
+}
+
+struct sshkey *
+get_hostkey_by_index(int ind)
+{
+       if (ind < 0 || (u_int)ind >= options.num_host_key_files)
+               return (NULL);
+       return (sensitive_data.host_keys[ind]);
+}
+
+struct sshkey *
+get_hostkey_public_by_index(int ind, struct ssh *ssh)
+{
+       if (ind < 0 || (u_int)ind >= options.num_host_key_files)
+               return (NULL);
+       return (sensitive_data.host_pubkeys[ind]);
+}
+
+int
+get_hostkey_index(struct sshkey *key, int compare, struct ssh *ssh)
+{
+       u_int i;
+
+       for (i = 0; i < options.num_host_key_files; i++) {
+               if (sshkey_is_cert(key)) {
+                       if (key == sensitive_data.host_certificates[i] ||
+                           (compare && sensitive_data.host_certificates[i] &&
+                           sshkey_equal(key,
+                           sensitive_data.host_certificates[i])))
+                               return (i);
+               } else {
+                       if (key == sensitive_data.host_keys[i] ||
+                           (compare && sensitive_data.host_keys[i] &&
+                           sshkey_equal(key, sensitive_data.host_keys[i])))
+                               return (i);
+                       if (key == sensitive_data.host_pubkeys[i] ||
+                           (compare && sensitive_data.host_pubkeys[i] &&
+                           sshkey_equal(key, sensitive_data.host_pubkeys[i])))
+                               return (i);
+               }
+       }
+       return (-1);
+}
+
+/* Inform the client of all hostkeys */
+static void
+notify_hostkeys(struct ssh *ssh)
+{
+       struct sshbuf *buf;
+       struct sshkey *key;
+       u_int i, nkeys;
+       int r;
+       char *fp;
+
+       /* Some clients cannot cope with the hostkeys message, skip those. */
+       if (ssh->compat & SSH_BUG_HOSTKEYS)
+               return;
+
+       if ((buf = sshbuf_new()) == NULL)
+               fatal_f("sshbuf_new");
+       for (i = nkeys = 0; i < options.num_host_key_files; i++) {
+               key = get_hostkey_public_by_index(i, ssh);
+               if (key == NULL || key->type == KEY_UNSPEC ||
+                   sshkey_is_cert(key))
+                       continue;
+               fp = sshkey_fingerprint(key, options.fingerprint_hash,
+                   SSH_FP_DEFAULT);
+               debug3_f("key %d: %s %s", i, sshkey_ssh_name(key), fp);
+               free(fp);
+               if (nkeys == 0) {
+                       /*
+                        * Start building the request when we find the
+                        * first usable key.
+                        */
+                       if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
+                           (r = sshpkt_put_cstring(ssh, "hostkeys-00@openssh.com")) != 0 ||
+                           (r = sshpkt_put_u8(ssh, 0)) != 0) /* want reply */
+                               sshpkt_fatal(ssh, r, "%s: start request", __func__);
+               }
+               /* Append the key to the request */
+               sshbuf_reset(buf);
+               if ((r = sshkey_putb(key, buf)) != 0)
+                       fatal_fr(r, "couldn't put hostkey %d", i);
+               if ((r = sshpkt_put_stringb(ssh, buf)) != 0)
+                       sshpkt_fatal(ssh, r, "%s: append key", __func__);
+               nkeys++;
+       }
+       debug3_f("sent %u hostkeys", nkeys);
+       if (nkeys == 0)
+               fatal_f("no hostkeys");
+       if ((r = sshpkt_send(ssh)) != 0)
+               sshpkt_fatal(ssh, r, "%s: send", __func__);
+       sshbuf_free(buf);
+}
+
+static void
+usage(void)
+{
+       fprintf(stderr, "%s, %s\n", SSH_VERSION, SSH_OPENSSL_VERSION);
+       fprintf(stderr,
+"usage: sshd [-46DdeGiqTtV] [-C connection_spec] [-c host_cert_file]\n"
+"            [-E log_file] [-f config_file] [-g login_grace_time]\n"
+"            [-h host_key_file] [-o option] [-p port] [-u len]\n"
+       );
+       exit(1);
+}
+
+static void
+parse_hostkeys(struct sshbuf *hostkeys)
+{
+       int r;
+       u_int num_keys = 0;
+       struct sshkey *k;
+       struct sshbuf *kbuf;
+       const u_char *cp;
+       size_t len;
+
+       while (sshbuf_len(hostkeys) != 0) {
+               if (num_keys > 2048)
+                       fatal_f("too many hostkeys");
+               sensitive_data.host_keys = xrecallocarray(
+                   sensitive_data.host_keys, num_keys, num_keys + 1,
+                   sizeof(*sensitive_data.host_pubkeys));
+               sensitive_data.host_pubkeys = xrecallocarray(
+                   sensitive_data.host_pubkeys, num_keys, num_keys + 1,
+                   sizeof(*sensitive_data.host_pubkeys));
+               sensitive_data.host_certificates = xrecallocarray(
+                   sensitive_data.host_certificates, num_keys, num_keys + 1,
+                   sizeof(*sensitive_data.host_certificates));
+               /* private key */
+               k = NULL;
+               if ((r = sshbuf_froms(hostkeys, &kbuf)) != 0)
+                       fatal_fr(r, "extract privkey");
+               if (sshbuf_len(kbuf) != 0 &&
+                   (r = sshkey_private_deserialize(kbuf, &k)) != 0)
+                       fatal_fr(r, "parse pubkey");
+               sensitive_data.host_keys[num_keys] = k;
+               sshbuf_free(kbuf);
+               if (k)
+                       debug2_f("privkey %u: %s", num_keys, sshkey_ssh_name(k));
+               /* public key */
+               k = NULL;
+               if ((r = sshbuf_get_string_direct(hostkeys, &cp, &len)) != 0)
+                       fatal_fr(r, "extract pubkey");
+               if (len != 0 && (r = sshkey_from_blob(cp, len, &k)) != 0)
+                       fatal_fr(r, "parse pubkey");
+               sensitive_data.host_pubkeys[num_keys] = k;
+               if (k)
+                       debug2_f("pubkey %u: %s", num_keys, sshkey_ssh_name(k));
+               /* certificate */
+               k = NULL;
+               if ((r = sshbuf_get_string_direct(hostkeys, &cp, &len)) != 0)
+                       fatal_fr(r, "extract pubkey");
+               if (len != 0 && (r = sshkey_from_blob(cp, len, &k)) != 0)
+                       fatal_fr(r, "parse pubkey");
+               sensitive_data.host_certificates[num_keys] = k;
+               if (k)
+                       debug2_f("cert %u: %s", num_keys, sshkey_ssh_name(k));
+               num_keys++;
+       }
+       sensitive_data.num_hostkeys = num_keys;
+}
+
+static void
+recv_rexec_state(int fd, struct sshbuf *conf, uint64_t *timing_secretp)
+{
+       struct sshbuf *m, *inc, *hostkeys;
+       u_char *cp, ver;
+       size_t len;
+       int r;
+       struct include_item *item;
+
+       debug3_f("entering fd = %d", fd);
+
+       if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL)
+               fatal_f("sshbuf_new failed");
+       if (ssh_msg_recv(fd, m) == -1)
+               fatal_f("ssh_msg_recv failed");
+       if ((r = sshbuf_get_u8(m, &ver)) != 0)
+               fatal_fr(r, "parse version");
+       if (ver != 0)
+               fatal_f("rexec version mismatch");
+       if ((r = sshbuf_get_string(m, &cp, &len)) != 0 || /* XXX _direct */
+           (r = sshbuf_get_u64(m, timing_secretp)) != 0 ||
+           (r = sshbuf_froms(m, &hostkeys)) != 0 ||
+           (r = sshbuf_get_stringb(m, inc)) != 0)
+               fatal_fr(r, "parse config");
+
+       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);
+       }
+
+       parse_hostkeys(hostkeys);
+
+       free(cp);
+       sshbuf_free(m);
+       sshbuf_free(hostkeys);
+       sshbuf_free(inc);
+
+       debug3_f("done");
+}
+
+/*
+ * If IP options are supported, make sure there are none (log and
+ * return an error if any are found).  Basically we are worried about
+ * source routing; it can be used to pretend you are somebody
+ * (ip-address) you are not. That itself may be "almost acceptable"
+ * under certain circumstances, but rhosts authentication is useless
+ * if source routing is accepted. Notice also that if we just dropped
+ * source routing here, the other side could use IP spoofing to do
+ * rest of the interaction and could still bypass security.  So we
+ * exit here if we detect any IP options.
+ */
+static void
+check_ip_options(struct ssh *ssh)
+{
+       int sock_in = ssh_packet_get_connection_in(ssh);
+       struct sockaddr_storage from;
+       u_char opts[200];
+       socklen_t i, option_size = sizeof(opts), fromlen = sizeof(from);
+       char text[sizeof(opts) * 3 + 1];
+
+       memset(&from, 0, sizeof(from));
+       if (getpeername(sock_in, (struct sockaddr *)&from,
+           &fromlen) == -1)
+               return;
+       if (from.ss_family != AF_INET)
+               return;
+       /* XXX IPv6 options? */
+
+       if (getsockopt(sock_in, IPPROTO_IP, IP_OPTIONS, opts,
+           &option_size) >= 0 && option_size != 0) {
+               text[0] = '\0';
+               for (i = 0; i < option_size; i++)
+                       snprintf(text + i*3, sizeof(text) - i*3,
+                           " %2.2x", opts[i]);
+               fatal("Connection from %.100s port %d with IP opts: %.800s",
+                   ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), text);
+       }
+       return;
+}
+
+/* Set the routing domain for this process */
+static void
+set_process_rdomain(struct ssh *ssh, const char *name)
+{
+       int rtable, ortable = getrtable();
+       const char *errstr;
+
+       if (name == NULL)
+               return; /* default */
+
+       if (strcmp(name, "%D") == 0) {
+               /* "expands" to routing domain of connection */
+               if ((name = ssh_packet_rdomain_in(ssh)) == NULL)
+                       return;
+       }
+
+       rtable = (int)strtonum(name, 0, 255, &errstr);
+       if (errstr != NULL) /* Shouldn't happen */
+               fatal("Invalid routing domain \"%s\": %s", name, errstr);
+       if (rtable != ortable && setrtable(rtable) != 0)
+               fatal("Unable to set routing domain %d: %s",
+                   rtable, strerror(errno));
+       debug_f("set routing domain %d (was %d)", rtable, ortable);
+}
+
+/*
+ * Main program for the daemon.
+ */
+int
+main(int ac, char **av)
+{
+       struct ssh *ssh = NULL;
+       extern char *optarg;
+       extern int optind;
+       int r, opt, on = 1, remote_port;
+       int sock_in = -1, sock_out = -1, rexeced_flag = 0, have_key = 0;
+       const char *remote_ip, *rdomain;
+       char *line, *laddr, *logfile = NULL;
+       u_int i;
+       u_int64_t ibytes, obytes;
+       mode_t new_umask;
+       Authctxt *authctxt;
+       struct connection_info *connection_info = NULL;
+       sigset_t sigmask;
+       uint64_t timing_secret = 0;
+
+       sigemptyset(&sigmask);
+       sigprocmask(SIG_SETMASK, &sigmask, NULL);
+
+       /* Save argv. */
+       saved_argv = av;
+
+       /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
+       sanitise_stdfd();
+
+       /* Initialize configuration options to their default values. */
+       initialize_server_options(&options);
+
+       /* Parse command-line arguments. */
+       while ((opt = getopt(ac, av,
+           "C:E:b:c:f:g:h:k:o:p:u:46DGQRTdeiqrtV")) != -1) {
+               switch (opt) {
+               case '4':
+                       options.address_family = AF_INET;
+                       break;
+               case '6':
+                       options.address_family = AF_INET6;
+                       break;
+               case 'f':
+                       config_file_name = optarg;
+                       break;
+               case 'c':
+                       servconf_add_hostcert("[command-line]", 0,
+                           &options, optarg);
+                       break;
+               case 'd':
+                       if (debug_flag == 0) {
+                               debug_flag = 1;
+                               options.log_level = SYSLOG_LEVEL_DEBUG1;
+                       } else if (options.log_level < SYSLOG_LEVEL_DEBUG3)
+                               options.log_level++;
+                       break;
+               case 'D':
+                       /* ignore */
+                       break;
+               case 'E':
+                       logfile = optarg;
+                       /* FALLTHROUGH */
+               case 'e':
+                       log_stderr = 1;
+                       break;
+               case 'i':
+                       inetd_flag = 1;
+                       break;
+               case 'r':
+                       /* ignore */
+                       break;
+               case 'R':
+                       rexeced_flag = 1;
+                       break;
+               case 'Q':
+                       /* ignored */
+                       break;
+               case 'q':
+                       options.log_level = SYSLOG_LEVEL_QUIET;
+                       break;
+               case 'b':
+                       /* protocol 1, ignored */
+                       break;
+               case 'p':
+                       options.ports_from_cmdline = 1;
+                       if (options.num_ports >= MAX_PORTS) {
+                               fprintf(stderr, "too many ports.\n");
+                               exit(1);
+                       }
+                       options.ports[options.num_ports++] = a2port(optarg);
+                       if (options.ports[options.num_ports-1] <= 0) {
+                               fprintf(stderr, "Bad port number.\n");
+                               exit(1);
+                       }
+                       break;
+               case 'g':
+                       if ((options.login_grace_time = convtime(optarg)) == -1) {
+                               fprintf(stderr, "Invalid login grace time.\n");
+                               exit(1);
+                       }
+                       break;
+               case 'k':
+                       /* protocol 1, ignored */
+                       break;
+               case 'h':
+                       servconf_add_hostkey("[command-line]", 0,
+                           &options, optarg, 1);
+                       break;
+               case 't':
+               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);
+                       break;
+               case 'u':
+                       utmp_len = (u_int)strtonum(optarg, 0, HOST_NAME_MAX+1+1, NULL);
+                       if (utmp_len > HOST_NAME_MAX+1) {
+                               fprintf(stderr, "Invalid utmp length.\n");
+                               exit(1);
+                       }
+                       break;
+               case 'o':
+                       line = xstrdup(optarg);
+                       if (process_server_config_line(&options, line,
+                           "command-line", 0, NULL, NULL, &includes) != 0)
+                               exit(1);
+                       free(line);
+                       break;
+               case 'V':
+                       fprintf(stderr, "%s, %s\n",
+                           SSH_VERSION, SSH_OPENSSL_VERSION);
+                       exit(0);
+               default:
+                       usage();
+                       break;
+               }
+       }
+
+       /* Check that there are no remaining arguments. */
+       if (optind < ac) {
+               fprintf(stderr, "Extra argument %s.\n", av[optind]);
+               exit(1);
+       }
+
+       debug("sshd version %s, %s", SSH_VERSION, SSH_OPENSSL_VERSION);
+
+       if (!rexeced_flag)
+               fatal("sshd-session should not be executed directly");
+
+       closefrom(REEXEC_MIN_FREE_FD);
+
+#ifdef WITH_OPENSSL
+       OpenSSL_add_all_algorithms();
+#endif
+
+       /* If requested, redirect the logs to the specified logfile. */
+       if (logfile != NULL) {
+               char *cp, pid_s[32];
+
+               snprintf(pid_s, sizeof(pid_s), "%ld", (unsigned long)getpid());
+               cp = percent_expand(logfile,
+                   "p", pid_s,
+                   "P", "sshd-session",
+                   (char *)NULL);
+               log_redirect_stderr_to(cp);
+               free(cp);
+       }
+
+       /*
+        * Force logging to stderr until we have loaded the private host
+        * key (unless started from inetd)
+        */
+       log_init(__progname,
+           options.log_level == SYSLOG_LEVEL_NOT_SET ?
+           SYSLOG_LEVEL_INFO : options.log_level,
+           options.log_facility == SYSLOG_FACILITY_NOT_SET ?
+           SYSLOG_FACILITY_AUTH : options.log_facility,
+           log_stderr || !inetd_flag || debug_flag);
+
+       debug("sshd version %s, %s", SSH_VERSION, SSH_OPENSSL_VERSION);
+
+       /* Fetch our configuration */
+       if ((cfg = sshbuf_new()) == NULL)
+               fatal("sshbuf_new config buf failed");
+       setproctitle("%s", "[rexeced]");
+       recv_rexec_state(REEXEC_CONFIG_PASS_FD, cfg, &timing_secret);
+       close(REEXEC_CONFIG_PASS_FD);
+       parse_server_config(&options, "rexec", cfg, &includes, NULL, 1);
+       /* Fill in default values for those options not explicitly set. */
+       fill_default_server_options(&options);
+       options.timing_secret = timing_secret;
+
+       if (!debug_flag) {
+               startup_pipe = dup(REEXEC_STARTUP_PIPE_FD);
+               close(REEXEC_STARTUP_PIPE_FD);
+               /*
+                * Signal parent that this child is at a point where
+                * they can go away if they have a SIGHUP pending.
+                */
+               (void)atomicio(vwrite, startup_pipe, "\0", 1);
+       }
+
+       /* Check that options are sensible */
+       if (options.authorized_keys_command_user == NULL &&
+           (options.authorized_keys_command != NULL &&
+           strcasecmp(options.authorized_keys_command, "none") != 0))
+               fatal("AuthorizedKeysCommand set without "
+                   "AuthorizedKeysCommandUser");
+       if (options.authorized_principals_command_user == NULL &&
+           (options.authorized_principals_command != NULL &&
+           strcasecmp(options.authorized_principals_command, "none") != 0))
+               fatal("AuthorizedPrincipalsCommand set without "
+                   "AuthorizedPrincipalsCommandUser");
+
+       /*
+        * Check whether there is any path through configured auth methods.
+        * Unfortunately it is not possible to verify this generally before
+        * daemonisation in the presence of Match block, but this catches
+        * and warns for trivial misconfigurations that could break login.
+        */
+       if (options.num_auth_methods != 0) {
+               for (i = 0; i < options.num_auth_methods; i++) {
+                       if (auth2_methods_valid(options.auth_methods[i],
+                           1) == 0)
+                               break;
+               }
+               if (i >= options.num_auth_methods)
+                       fatal("AuthenticationMethods cannot be satisfied by "
+                           "enabled authentication methods");
+       }
+
+#ifdef WITH_OPENSSL
+       if (options.moduli_file != NULL)
+               dh_set_moduli_file(options.moduli_file);
+#endif
+
+       if (options.host_key_agent) {
+               if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME))
+                       setenv(SSH_AUTHSOCKET_ENV_NAME,
+                           options.host_key_agent, 1);
+               if ((r = ssh_get_authentication_socket(NULL)) == 0)
+                       have_agent = 1;
+               else
+                       error_r(r, "Could not connect to agent \"%s\"",
+                           options.host_key_agent);
+       }
+
+       if (options.num_host_key_files != sensitive_data.num_hostkeys) {
+               fatal("internal error: hostkeys confused (config %u recvd %u)",
+                   options.num_host_key_files, sensitive_data.num_hostkeys);
+       }
+
+       for (i = 0; i < options.num_host_key_files; i++) {
+               if (sensitive_data.host_keys[i] != NULL ||
+                   (have_agent && sensitive_data.host_pubkeys[i] != NULL)) {
+                       have_key = 1;
+                       break;
+               }
+       }
+       if (!have_key)
+               fatal("internal error: monitor recieved no hostkeys");
+
+       /* Ensure that umask disallows at least group and world write */
+       new_umask = umask(0077) | 0022;
+       (void) umask(new_umask);
+
+       /* Initialize the log (it is reinitialized below in case we forked). */
+       if (debug_flag)
+               log_stderr = 1;
+       log_init(__progname, options.log_level,
+           options.log_facility, log_stderr);
+       for (i = 0; i < options.num_log_verbose; i++)
+               log_verbose_add(options.log_verbose[i]);
+
+       /* Reinitialize the log (because of the fork above). */
+       log_init(__progname, options.log_level, options.log_facility, log_stderr);
+
+       /*
+        * Chdir to the root directory so that the current disk can be
+        * unmounted if desired.
+        */
+       if (chdir("/") == -1)
+               error("chdir(\"/\"): %s", strerror(errno));
+
+       /* ignore SIGPIPE */
+       ssh_signal(SIGPIPE, SIG_IGN);
+
+       /* Get a connection, either from inetd or rexec */
+       if (inetd_flag) {
+               /*
+                * NB. must be different fd numbers for the !socket case,
+                * as packet_connection_is_on_socket() depends on this.
+                */
+               sock_in = dup(STDIN_FILENO);
+               sock_out = dup(STDOUT_FILENO);
+       } else {
+               /* rexec case; accept()ed socket in ancestor listener */
+               sock_in = sock_out = dup(STDIN_FILENO);
+       }
+
+       /*
+        * We intentionally do not close the descriptors 0, 1, and 2
+        * as our code for setting the descriptors won't work if
+        * ttyfd happens to be one of those.
+        */
+       if (stdfd_devnull(1, 1, !log_stderr) == -1)
+               error("stdfd_devnull failed");
+       debug("network sockets: %d, %d", sock_in, sock_out);
+
+       /* This is the child processing a new connection. */
+       setproctitle("%s", "[accepted]");
+
+       /* Executed child processes don't need these. */
+       fcntl(sock_out, F_SETFD, FD_CLOEXEC);
+       fcntl(sock_in, F_SETFD, FD_CLOEXEC);
+
+       /* We will not restart on SIGHUP since it no longer makes sense. */
+       ssh_signal(SIGALRM, SIG_DFL);
+       ssh_signal(SIGHUP, SIG_DFL);
+       ssh_signal(SIGTERM, SIG_DFL);
+       ssh_signal(SIGQUIT, SIG_DFL);
+       ssh_signal(SIGCHLD, SIG_DFL);
+
+       /*
+        * Register our connection.  This turns encryption off because we do
+        * not have a key.
+        */
+       if ((ssh = ssh_packet_set_connection(NULL, sock_in, sock_out)) == NULL)
+               fatal("Unable to create connection");
+       the_active_state = ssh;
+       ssh_packet_set_server(ssh);
+
+       check_ip_options(ssh);
+
+       /* Prepare the channels layer */
+       channel_init_channels(ssh);
+       channel_set_af(ssh, options.address_family);
+       server_process_channel_timeouts(ssh);
+       server_process_permitopen(ssh);
+
+       /* Set SO_KEEPALIVE if requested. */
+       if (options.tcp_keep_alive && ssh_packet_connection_is_on_socket(ssh) &&
+           setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1)
+               error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
+
+       if ((remote_port = ssh_remote_port(ssh)) < 0) {
+               debug("ssh_remote_port failed");
+               cleanup_exit(255);
+       }
+
+       /*
+        * The rest of the code depends on the fact that
+        * ssh_remote_ipaddr() caches the remote ip, even if
+        * the socket goes away.
+        */
+       remote_ip = ssh_remote_ipaddr(ssh);
+
+       rdomain = ssh_packet_rdomain_in(ssh);
+
+       /* Log the connection. */
+       laddr = get_local_ipaddr(sock_in);
+       verbose("Connection from %s port %d on %s port %d%s%s%s",
+           remote_ip, remote_port, laddr,  ssh_local_port(ssh),
+           rdomain == NULL ? "" : " rdomain \"",
+           rdomain == NULL ? "" : rdomain,
+           rdomain == NULL ? "" : "\"");
+       free(laddr);
+
+       /*
+        * We don't want to listen forever unless the other side
+        * successfully authenticates itself.  So we set up an alarm which is
+        * cleared after successful authentication.  A limit of zero
+        * indicates no limit. Note that we don't set the alarm in debugging
+        * mode; it is just annoying to have the server exit just when you
+        * are about to discover the bug.
+        */
+       ssh_signal(SIGALRM, grace_alarm_handler);
+       if (!debug_flag)
+               alarm(options.login_grace_time);
+
+       if ((r = kex_exchange_identification(ssh, -1,
+           options.version_addendum)) != 0)
+               sshpkt_fatal(ssh, r, "banner exchange");
+
+       ssh_packet_set_nonblocking(ssh);
+
+       /* allocate authentication context */
+       authctxt = xcalloc(1, sizeof(*authctxt));
+       ssh->authctxt = authctxt;
+
+       /* XXX global for cleanup, access from other modules */
+       the_authctxt = authctxt;
+
+       /* Set default key authentication options */
+       if ((auth_opts = sshauthopt_new_with_keys_defaults()) == NULL)
+               fatal("allocation failed");
+
+       /* prepare buffer to collect messages to display to user after login */
+       if ((loginmsg = sshbuf_new()) == NULL)
+               fatal("sshbuf_new loginmsg failed");
+       auth_debug_reset();
+
+       if (privsep_preauth(ssh) == 1)
+               goto authenticated;
+
+       /* perform the key exchange */
+       /* authenticate user and start session */
+       do_ssh2_kex(ssh);
+       do_authentication2(ssh);
+
+       /*
+        * The unprivileged child now transfers the current keystate and exits.
+        */
+       mm_send_keystate(ssh, pmonitor);
+       ssh_packet_clear_keys(ssh);
+       exit(0);
+
+ authenticated:
+       /*
+        * Cancel the alarm we set to limit the time taken for
+        * authentication.
+        */
+       alarm(0);
+       ssh_signal(SIGALRM, SIG_DFL);
+       authctxt->authenticated = 1;
+       if (startup_pipe != -1) {
+               close(startup_pipe);
+               startup_pipe = -1;
+       }
+
+       if (options.routing_domain != NULL)
+               set_process_rdomain(ssh, options.routing_domain);
+
+       /*
+        * In privilege separation, we fork another child and prepare
+        * file descriptor passing.
+        */
+       privsep_postauth(ssh, authctxt);
+       /* the monitor process [priv] will not return */
+
+       ssh_packet_set_timeout(ssh, options.client_alive_interval,
+           options.client_alive_count_max);
+
+       /* Try to send all our hostkeys to the client */
+       notify_hostkeys(ssh);
+
+       /* Start session. */
+       do_authenticated(ssh, authctxt);
+
+       /* The connection has been terminated. */
+       ssh_packet_get_bytes(ssh, &ibytes, &obytes);
+       verbose("Transferred: sent %llu, received %llu bytes",
+           (unsigned long long)obytes, (unsigned long long)ibytes);
+
+       verbose("Closing connection to %.500s port %d", remote_ip, remote_port);
+       ssh_packet_close(ssh);
+
+       mm_terminate();
+
+       exit(0);
+}
+
+int
+sshd_hostkey_sign(struct ssh *ssh, struct sshkey *privkey,
+    struct sshkey *pubkey, u_char **signature, size_t *slenp,
+    const u_char *data, size_t dlen, const char *alg)
+{
+       if (privkey) {
+               if (mm_sshkey_sign(ssh, privkey, signature, slenp,
+                   data, dlen, alg, options.sk_provider, NULL,
+                   ssh->compat) < 0)
+                       fatal_f("privkey sign failed");
+       } else {
+               if (mm_sshkey_sign(ssh, pubkey, signature, slenp,
+                   data, dlen, alg, options.sk_provider, NULL,
+                   ssh->compat) < 0)
+                       fatal_f("pubkey sign failed");
+       }
+       return 0;
+}
+
+/* SSH2 key exchange */
+static void
+do_ssh2_kex(struct ssh *ssh)
+{
+       char *hkalgs = NULL, *myproposal[PROPOSAL_MAX];
+       const char *compression = NULL;
+       struct kex *kex;
+       int r;
+
+       if (options.rekey_limit || options.rekey_interval)
+               ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
+                   options.rekey_interval);
+
+       if (options.compression == COMP_NONE)
+               compression = "none";
+       hkalgs = list_hostkey_types();
+
+       kex_proposal_populate_entries(ssh, myproposal, options.kex_algorithms,
+           options.ciphers, options.macs, compression, hkalgs);
+
+       free(hkalgs);
+
+       /* start key exchange */
+       if ((r = kex_setup(ssh, myproposal)) != 0)
+               fatal_r(r, "kex_setup");
+       kex_set_server_sig_algs(ssh, options.pubkey_accepted_algos);
+       kex = ssh->kex;
+
+#ifdef WITH_OPENSSL
+       kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server;
+       kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server;
+       kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_server;
+       kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_server;
+       kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_server;
+       kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
+       kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
+       kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
+#endif
+       kex->kex[KEX_C25519_SHA256] = kex_gen_server;
+       kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
+       kex->load_host_public_key=&get_hostkey_public_by_type;
+       kex->load_host_private_key=&get_hostkey_private_by_type;
+       kex->host_key_index=&get_hostkey_index;
+       kex->sign = sshd_hostkey_sign;
+
+       ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &kex->done);
+       kex_proposal_free_entries(myproposal);
+
+#ifdef DEBUG_KEXDH
+       /* send 1st encrypted/maced/compressed message */
+       if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 ||
+           (r = sshpkt_put_cstring(ssh, "markus")) != 0 ||
+           (r = sshpkt_send(ssh)) != 0 ||
+           (r = ssh_packet_write_wait(ssh)) != 0)
+               fatal_fr(r, "send test");
+#endif
+       debug("KEX done");
+}
+
+/* server specific fatal cleanup */
+void
+cleanup_exit(int i)
+{
+       if (the_active_state != NULL && the_authctxt != NULL) {
+               do_cleanup(the_active_state, the_authctxt);
+               if (privsep_is_preauth &&
+                   pmonitor != NULL && pmonitor->m_pid > 1) {
+                       debug("Killing privsep child %d", pmonitor->m_pid);
+                       if (kill(pmonitor->m_pid, SIGKILL) != 0 &&
+                           errno != ESRCH) {
+                               error_f("kill(%d): %s", pmonitor->m_pid,
+                                   strerror(errno));
+                       }
+               }
+       }
+       _exit(i);
+}
diff --git a/usr.bin/ssh/sshd-session/Makefile b/usr.bin/ssh/sshd-session/Makefile
new file mode 100644 (file)
index 0000000..0ca4add
--- /dev/null
@@ -0,0 +1,71 @@
+#      $OpenBSD: Makefile,v 1.1 2024/05/17 00:30:24 djm Exp $
+
+.PATH:         ${.CURDIR}/..
+
+SRCS=  sshd-session.c auth2-methods.c \
+       auth-rhosts.c auth-passwd.c sshpty.c sshlogin.c servconf.c \
+       serverloop.c auth.c auth2.c auth-options.c session.c auth2-chall.c \
+       groupaccess.c auth-bsdauth.c auth2-hostbased.c auth2-kbdint.c \
+       auth2-none.c auth2-passwd.c auth2-pubkey.c auth2-pubkeyfile.c \
+       monitor.c monitor_wrap.c \
+       sftp-server.c sftp-common.c sftp-realpath.c sandbox-pledge.c srclimit.c
+SRCS+= authfd.c compat.c dns.c fatal.c hostfile.c readpass.c utf8.c uidswap.c
+SRCS+= ${SRCS_BASE} ${SRCS_KEX} ${SRCS_KEXS} ${SRCS_KEY} ${SRCS_KEYP} \
+       ${SRCS_KRL} ${SRCS_PROT} ${SRCS_PKT} ${SRCS_UTL} ${SRCS_PKCS11} \
+       ${SRCS_SK_CLIENT}
+
+PROG=  sshd-session
+BINDIR=        /usr/libexec
+BINMODE=511
+NOMAN= 1
+
+.include <bsd.own.mk> # for KERBEROS and AFS
+
+KERBEROS5=no
+
+.if (${KERBEROS5:L} == "yes")
+CFLAGS+=-DKRB5 -I${DESTDIR}/usr/include/kerberosV -DGSSAPI
+SRCS+=  auth-krb5.c auth2-gss.c gss-serv.c gss-serv-krb5.c
+.endif
+
+.include <bsd.prog.mk>
+
+.if (${KERBEROS5:L} == "yes")
+LDADD+= -lgssapi -lkrb5 -lasn1
+LDADD+= -lwind -lroken -lcom_err -lpthread -lheimbase -lkafs
+DPADD+= ${LIBGSSAPI} ${LIBKRB5}
+.endif
+
+.if (${OPENSSL:L} == "yes")
+LDADD+=        -lcrypto
+DPADD+=        ${LIBCRYPTO}
+.endif
+
+LDADD+=        -lutil
+DPADD+=        ${LIBUTIL}
+
+.if (${ZLIB:L} == "yes")
+LDADD+=        -lz
+DPADD+=        ${LIBZ}
+.endif
+
+# The random relink kit, used on OpenBSD by /etc/rc
+
+CLEANFILES+= ${PROG}.tar install.sh
+
+install.sh: Makefile
+       echo "set -o errexit" > $@
+       echo "${CC} ${LDFLAGS} ${LDSTATIC} -o ${PROG}" \
+           "\`echo " ${OBJS} "| tr ' ' '\\\n' | sort -R\`" ${LDADD} >> $@
+       echo "./${PROG} -V # test it works" >> $@
+       echo "install -c -s  -o root -g bin -m ${BINMODE} ${PROG} " \
+           "${BINDIR}/${PROG}" >> $@
+
+${PROG}.tar: ${OBJS} install.sh
+       tar cf $@ ${OBJS} install.sh
+
+afterinstall: ${PROG}.tar
+       install -d -o root -g wheel -m 755 \
+           ${DESTDIR}/usr/share/relink/${BINDIR}/${PROG}
+       install -o ${BINOWN} -g ${BINGRP} -m 640 \
+           ${PROG}.tar ${DESTDIR}/usr/share/relink/${BINDIR}/${PROG}/${PROG}.tar
index a5d56e9..683b0b8 100644 (file)
@@ -1,23 +1,5 @@
-/* $OpenBSD: sshd.c,v 1.602 2024/01/08 00:34:34 djm Exp $ */
+/* $OpenBSD: sshd.c,v 1.603 2024/05/17 00:30:24 djm Exp $ */
 /*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
- * This program is the ssh daemon.  It listens for connections from clients,
- * and performs authentication, executes use commands or shell, and forwards
- * information to/from the application to the user client over an encrypted
- * connection.  This can also handle forwarding of X11, TCP/IP, and
- * authentication agent connections.
- *
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose.  Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
- *
- * SSH2 implementation:
- * Privilege Separation:
- *
  * Copyright (c) 2000, 2001, 2002 Markus Friedl.  All rights reserved.
  * Copyright (c) 2002 Niels Provos.  All rights reserved.
  *
 
 #ifdef WITH_OPENSSL
 #include <openssl/bn.h>
+#include <openssl/evp.h>
 #endif
 
 #include "xmalloc.h"
 #include "ssh.h"
-#include "ssh2.h"
 #include "sshpty.h"
-#include "packet.h"
 #include "log.h"
 #include "sshbuf.h"
 #include "misc.h"
-#include "match.h"
 #include "servconf.h"
-#include "uidswap.h"
 #include "compat.h"
-#include "cipher.h"
 #include "digest.h"
 #include "sshkey.h"
-#include "kex.h"
 #include "authfile.h"
 #include "pathnames.h"
-#include "atomicio.h"
 #include "canohost.h"
 #include "hostfile.h"
 #include "auth.h"
 #include "authfd.h"
 #include "msg.h"
-#include "dispatch.h"
-#include "channels.h"
-#include "session.h"
-#include "monitor.h"
-#ifdef GSSAPI
-#include "ssh-gss.h"
-#endif
-#include "monitor_wrap.h"
-#include "ssh-sandbox.h"
-#include "auth-options.h"
 #include "version.h"
 #include "ssherr.h"
 #include "sk-api.h"
 #include "srclimit.h"
-#include "dh.h"
 
 /* Re-exec fds */
 #define REEXEC_DEVCRYPTO_RESERVED_FD   (STDERR_FILENO + 1)
@@ -120,9 +85,6 @@ extern char *__progname;
 /* Server configuration options. */
 ServerOptions options;
 
-/* Name of the server configuration file. */
-char *config_file_name = _PATH_SERVER_CONFIG_FILE;
-
 /*
  * Debug mode flag.  This can be set on the command line.  If debug
  * mode is enabled, extra debugging output will be sent to the system
@@ -131,32 +93,9 @@ char *config_file_name = _PATH_SERVER_CONFIG_FILE;
  */
 int debug_flag = 0;
 
-/*
- * Indicating that the daemon should only test the configuration and keys.
- * If test_flag > 1 ("-T" flag), then sshd will also dump the effective
- * configuration, optionally using connection information provided by the
- * "-C" flag.
- */
-static int test_flag = 0;
-
-/* Flag indicating that the daemon is being started from inetd. */
-static int inetd_flag = 0;
-
-/* Flag indicating that sshd should not detach and become a daemon. */
-static int no_daemon_flag = 0;
-
-/* debug goes to stderr unless inetd_flag is set */
-static int log_stderr = 0;
-
 /* Saved arguments to main(). */
 static char **saved_argv;
 
-/* re-exec */
-static int rexeced_flag = 0;
-static int rexec_flag = 1;
-static int rexec_argc = 0;
-static char **rexec_argv;
-
 /*
  * The sockets that the server is listening; this is used in the SIGHUP
  * signal handler.
@@ -165,10 +104,6 @@ static char **rexec_argv;
 static int listen_socks[MAX_LISTEN_SOCKS];
 static int num_listen_socks = 0;
 
-/* Daemon's agent connection */
-int auth_sock = -1;
-static int have_agent = 0;
-
 /*
  * Any really sensitive data in the application is contained in this
  * structure. The idea is that this structure could be locked into memory so
@@ -212,18 +147,6 @@ static int *startup_pipes = NULL;
 static int *startup_flags = NULL;      /* Indicates child closed listener */
 static int startup_pipe = -1;          /* in child */
 
-/* variables used for privilege separation */
-int use_privsep = -1;
-struct monitor *pmonitor = NULL;
-int privsep_is_preauth = 1;
-
-/* global connection state and authentication contexts */
-Authctxt *the_authctxt = NULL;
-struct ssh *the_active_state;
-
-/* global key/cert auth options. XXX move to permanent ssh->authctxt? */
-struct sshauthopt *auth_opts = NULL;
-
 /* sshd_config buffer */
 struct sshbuf *cfg;
 
@@ -233,11 +156,6 @@ struct include_list includes = TAILQ_HEAD_INITIALIZER(includes);
 /* message to be displayed after login */
 struct sshbuf *loginmsg;
 
-/* Prototypes for various functions defined later in this file. */
-void destroy_sensitive_data(void);
-void demote_sensitive_data(void);
-static void do_ssh2_kex(struct ssh *);
-
 static char *listener_proctitle;
 
 /*
@@ -321,446 +239,6 @@ main_sigchld_handler(int sig)
        errno = save_errno;
 }
 
-/*
- * Signal handler for the alarm after the login grace period has expired.
- */
-static void
-grace_alarm_handler(int sig)
-{
-       /*
-        * Try to kill any processes that we have spawned, E.g. authorized
-        * keys command helpers or privsep children.
-        */
-       if (getpgid(0) == getpid()) {
-               ssh_signal(SIGTERM, SIG_IGN);
-               kill(0, SIGTERM);
-       }
-
-       /* Log error and exit. */
-       sigdie("Timeout before authentication for %s port %d",
-           ssh_remote_ipaddr(the_active_state),
-           ssh_remote_port(the_active_state));
-}
-
-/* Destroy the host and server keys.  They will no longer be needed. */
-void
-destroy_sensitive_data(void)
-{
-       u_int i;
-
-       for (i = 0; i < options.num_host_key_files; i++) {
-               if (sensitive_data.host_keys[i]) {
-                       sshkey_free(sensitive_data.host_keys[i]);
-                       sensitive_data.host_keys[i] = NULL;
-               }
-               if (sensitive_data.host_certificates[i]) {
-                       sshkey_free(sensitive_data.host_certificates[i]);
-                       sensitive_data.host_certificates[i] = NULL;
-               }
-       }
-}
-
-/* Demote private to public keys for network child */
-void
-demote_sensitive_data(void)
-{
-       struct sshkey *tmp;
-       u_int i;
-       int r;
-
-       for (i = 0; i < options.num_host_key_files; i++) {
-               if (sensitive_data.host_keys[i]) {
-                       if ((r = sshkey_from_private(
-                           sensitive_data.host_keys[i], &tmp)) != 0)
-                               fatal_r(r, "could not demote host %s key",
-                                   sshkey_type(sensitive_data.host_keys[i]));
-                       sshkey_free(sensitive_data.host_keys[i]);
-                       sensitive_data.host_keys[i] = tmp;
-               }
-               /* Certs do not need demotion */
-       }
-}
-
-static void
-privsep_preauth_child(void)
-{
-       gid_t gidset[1];
-       struct passwd *pw;
-
-       /* Enable challenge-response authentication for privilege separation */
-       privsep_challenge_enable();
-
-#ifdef GSSAPI
-       /* Cache supported mechanism OIDs for later use */
-       ssh_gssapi_prepare_supported_oids();
-#endif
-
-       /* Demote the private keys to public keys. */
-       demote_sensitive_data();
-
-       /* Demote the child */
-       if (getuid() == 0 || geteuid() == 0) {
-               if ((pw = getpwnam(SSH_PRIVSEP_USER)) == NULL)
-                       fatal("Privilege separation user %s does not exist",
-                           SSH_PRIVSEP_USER);
-               pw = pwcopy(pw); /* Ensure mutable */
-               endpwent();
-               freezero(pw->pw_passwd, strlen(pw->pw_passwd));
-
-               /* Change our root directory */
-               if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1)
-                       fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR,
-                           strerror(errno));
-               if (chdir("/") == -1)
-                       fatal("chdir(\"/\"): %s", strerror(errno));
-
-               /*
-                * Drop our privileges
-                * NB. Can't use setusercontext() after chroot.
-                */
-               debug3("privsep user:group %u:%u", (u_int)pw->pw_uid,
-                   (u_int)pw->pw_gid);
-               gidset[0] = pw->pw_gid;
-               if (setgroups(1, gidset) == -1)
-                       fatal("setgroups: %.100s", strerror(errno));
-               permanently_set_uid(pw);
-       }
-}
-
-static int
-privsep_preauth(struct ssh *ssh)
-{
-       int status, r;
-       pid_t pid;
-       struct ssh_sandbox *box = NULL;
-
-       /* Set up unprivileged child process to deal with network data */
-       pmonitor = monitor_init();
-       /* Store a pointer to the kex for later rekeying */
-       pmonitor->m_pkex = &ssh->kex;
-
-       if (use_privsep == PRIVSEP_ON)
-               box = ssh_sandbox_init();
-       pid = fork();
-       if (pid == -1) {
-               fatal("fork of unprivileged child failed");
-       } else if (pid != 0) {
-               debug2("Network child is on pid %ld", (long)pid);
-
-               pmonitor->m_pid = pid;
-               if (have_agent) {
-                       r = ssh_get_authentication_socket(&auth_sock);
-                       if (r != 0) {
-                               error_r(r, "Could not get agent socket");
-                               have_agent = 0;
-                       }
-               }
-               if (box != NULL)
-                       ssh_sandbox_parent_preauth(box, pid);
-               monitor_child_preauth(ssh, pmonitor);
-
-               /* Wait for the child's exit status */
-               while (waitpid(pid, &status, 0) == -1) {
-                       if (errno == EINTR)
-                               continue;
-                       pmonitor->m_pid = -1;
-                       fatal_f("waitpid: %s", strerror(errno));
-               }
-               privsep_is_preauth = 0;
-               pmonitor->m_pid = -1;
-               if (WIFEXITED(status)) {
-                       if (WEXITSTATUS(status) != 0)
-                               fatal_f("preauth child exited with status %d",
-                                   WEXITSTATUS(status));
-               } else if (WIFSIGNALED(status))
-                       fatal_f("preauth child terminated by signal %d",
-                           WTERMSIG(status));
-               if (box != NULL)
-                       ssh_sandbox_parent_finish(box);
-               return 1;
-       } else {
-               /* child */
-               close(pmonitor->m_sendfd);
-               close(pmonitor->m_log_recvfd);
-
-               /* Arrange for logging to be sent to the monitor */
-               set_log_handler(mm_log_handler, pmonitor);
-
-               privsep_preauth_child();
-               setproctitle("%s", "[net]");
-               if (box != NULL)
-                       ssh_sandbox_child(box);
-
-               return 0;
-       }
-}
-
-static void
-privsep_postauth(struct ssh *ssh, Authctxt *authctxt)
-{
-       if (authctxt->pw->pw_uid == 0) {
-               /* File descriptor passing is broken or root login */
-               use_privsep = 0;
-               goto skip;
-       }
-
-       /* New socket pair */
-       monitor_reinit(pmonitor);
-
-       pmonitor->m_pid = fork();
-       if (pmonitor->m_pid == -1)
-               fatal("fork of unprivileged child failed");
-       else if (pmonitor->m_pid != 0) {
-               verbose("User child is on pid %ld", (long)pmonitor->m_pid);
-               sshbuf_reset(loginmsg);
-               monitor_clear_keystate(ssh, pmonitor);
-               monitor_child_postauth(ssh, pmonitor);
-
-               /* NEVERREACHED */
-               exit(0);
-       }
-
-       /* child */
-
-       close(pmonitor->m_sendfd);
-       pmonitor->m_sendfd = -1;
-
-       /* Demote the private keys to public keys. */
-       demote_sensitive_data();
-
-       /* Drop privileges */
-       do_setusercontext(authctxt->pw);
-
- skip:
-       /* It is safe now to apply the key state */
-       monitor_apply_keystate(ssh, pmonitor);
-
-       /*
-        * Tell the packet layer that authentication was successful, since
-        * this information is not part of the key state.
-        */
-       ssh_packet_set_authenticated(ssh);
-}
-
-static void
-append_hostkey_type(struct sshbuf *b, const char *s)
-{
-       int r;
-
-       if (match_pattern_list(s, options.hostkeyalgorithms, 0) != 1) {
-               debug3_f("%s key not permitted by HostkeyAlgorithms", s);
-               return;
-       }
-       if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) > 0 ? "," : "", s)) != 0)
-               fatal_fr(r, "sshbuf_putf");
-}
-
-static char *
-list_hostkey_types(void)
-{
-       struct sshbuf *b;
-       struct sshkey *key;
-       char *ret;
-       u_int i;
-
-       if ((b = sshbuf_new()) == NULL)
-               fatal_f("sshbuf_new failed");
-       for (i = 0; i < options.num_host_key_files; i++) {
-               key = sensitive_data.host_keys[i];
-               if (key == NULL)
-                       key = sensitive_data.host_pubkeys[i];
-               if (key == NULL)
-                       continue;
-               switch (key->type) {
-               case KEY_RSA:
-                       /* for RSA we also support SHA2 signatures */
-                       append_hostkey_type(b, "rsa-sha2-512");
-                       append_hostkey_type(b, "rsa-sha2-256");
-                       /* FALLTHROUGH */
-               case KEY_DSA:
-               case KEY_ECDSA:
-               case KEY_ED25519:
-               case KEY_ECDSA_SK:
-               case KEY_ED25519_SK:
-               case KEY_XMSS:
-                       append_hostkey_type(b, sshkey_ssh_name(key));
-                       break;
-               }
-               /* If the private key has a cert peer, then list that too */
-               key = sensitive_data.host_certificates[i];
-               if (key == NULL)
-                       continue;
-               switch (key->type) {
-               case KEY_RSA_CERT:
-                       /* for RSA we also support SHA2 signatures */
-                       append_hostkey_type(b,
-                           "rsa-sha2-512-cert-v01@openssh.com");
-                       append_hostkey_type(b,
-                           "rsa-sha2-256-cert-v01@openssh.com");
-                       /* FALLTHROUGH */
-               case KEY_DSA_CERT:
-               case KEY_ECDSA_CERT:
-               case KEY_ED25519_CERT:
-               case KEY_ECDSA_SK_CERT:
-               case KEY_ED25519_SK_CERT:
-               case KEY_XMSS_CERT:
-                       append_hostkey_type(b, sshkey_ssh_name(key));
-                       break;
-               }
-       }
-       if ((ret = sshbuf_dup_string(b)) == NULL)
-               fatal_f("sshbuf_dup_string failed");
-       sshbuf_free(b);
-       debug_f("%s", ret);
-       return ret;
-}
-
-static struct sshkey *
-get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
-{
-       u_int i;
-       struct sshkey *key;
-
-       for (i = 0; i < options.num_host_key_files; i++) {
-               switch (type) {
-               case KEY_RSA_CERT:
-               case KEY_DSA_CERT:
-               case KEY_ECDSA_CERT:
-               case KEY_ED25519_CERT:
-               case KEY_ECDSA_SK_CERT:
-               case KEY_ED25519_SK_CERT:
-               case KEY_XMSS_CERT:
-                       key = sensitive_data.host_certificates[i];
-                       break;
-               default:
-                       key = sensitive_data.host_keys[i];
-                       if (key == NULL && !need_private)
-                               key = sensitive_data.host_pubkeys[i];
-                       break;
-               }
-               if (key == NULL || key->type != type)
-                       continue;
-               switch (type) {
-               case KEY_ECDSA:
-               case KEY_ECDSA_SK:
-               case KEY_ECDSA_CERT:
-               case KEY_ECDSA_SK_CERT:
-                       if (key->ecdsa_nid != nid)
-                               continue;
-                       /* FALLTHROUGH */
-               default:
-                       return need_private ?
-                           sensitive_data.host_keys[i] : key;
-               }
-       }
-       return NULL;
-}
-
-struct sshkey *
-get_hostkey_public_by_type(int type, int nid, struct ssh *ssh)
-{
-       return get_hostkey_by_type(type, nid, 0, ssh);
-}
-
-struct sshkey *
-get_hostkey_private_by_type(int type, int nid, struct ssh *ssh)
-{
-       return get_hostkey_by_type(type, nid, 1, ssh);
-}
-
-struct sshkey *
-get_hostkey_by_index(int ind)
-{
-       if (ind < 0 || (u_int)ind >= options.num_host_key_files)
-               return (NULL);
-       return (sensitive_data.host_keys[ind]);
-}
-
-struct sshkey *
-get_hostkey_public_by_index(int ind, struct ssh *ssh)
-{
-       if (ind < 0 || (u_int)ind >= options.num_host_key_files)
-               return (NULL);
-       return (sensitive_data.host_pubkeys[ind]);
-}
-
-int
-get_hostkey_index(struct sshkey *key, int compare, struct ssh *ssh)
-{
-       u_int i;
-
-       for (i = 0; i < options.num_host_key_files; i++) {
-               if (sshkey_is_cert(key)) {
-                       if (key == sensitive_data.host_certificates[i] ||
-                           (compare && sensitive_data.host_certificates[i] &&
-                           sshkey_equal(key,
-                           sensitive_data.host_certificates[i])))
-                               return (i);
-               } else {
-                       if (key == sensitive_data.host_keys[i] ||
-                           (compare && sensitive_data.host_keys[i] &&
-                           sshkey_equal(key, sensitive_data.host_keys[i])))
-                               return (i);
-                       if (key == sensitive_data.host_pubkeys[i] ||
-                           (compare && sensitive_data.host_pubkeys[i] &&
-                           sshkey_equal(key, sensitive_data.host_pubkeys[i])))
-                               return (i);
-               }
-       }
-       return (-1);
-}
-
-/* Inform the client of all hostkeys */
-static void
-notify_hostkeys(struct ssh *ssh)
-{
-       struct sshbuf *buf;
-       struct sshkey *key;
-       u_int i, nkeys;
-       int r;
-       char *fp;
-
-       /* Some clients cannot cope with the hostkeys message, skip those. */
-       if (ssh->compat & SSH_BUG_HOSTKEYS)
-               return;
-
-       if ((buf = sshbuf_new()) == NULL)
-               fatal_f("sshbuf_new");
-       for (i = nkeys = 0; i < options.num_host_key_files; i++) {
-               key = get_hostkey_public_by_index(i, ssh);
-               if (key == NULL || key->type == KEY_UNSPEC ||
-                   sshkey_is_cert(key))
-                       continue;
-               fp = sshkey_fingerprint(key, options.fingerprint_hash,
-                   SSH_FP_DEFAULT);
-               debug3_f("key %d: %s %s", i, sshkey_ssh_name(key), fp);
-               free(fp);
-               if (nkeys == 0) {
-                       /*
-                        * Start building the request when we find the
-                        * first usable key.
-                        */
-                       if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
-                           (r = sshpkt_put_cstring(ssh, "hostkeys-00@openssh.com")) != 0 ||
-                           (r = sshpkt_put_u8(ssh, 0)) != 0) /* want reply */
-                               sshpkt_fatal(ssh, r, "%s: start request", __func__);
-               }
-               /* Append the key to the request */
-               sshbuf_reset(buf);
-               if ((r = sshkey_putb(key, buf)) != 0)
-                       fatal_fr(r, "couldn't put hostkey %d", i);
-               if ((r = sshpkt_put_stringb(ssh, buf)) != 0)
-                       sshpkt_fatal(ssh, r, "%s: append key", __func__);
-               nkeys++;
-       }
-       debug3_f("sent %u hostkeys", nkeys);
-       if (nkeys == 0)
-               fatal_f("no hostkeys");
-       if ((r = sshpkt_send(ssh)) != 0)
-               sshpkt_fatal(ssh, r, "%s: send", __func__);
-       sshbuf_free(buf);
-}
-
 /*
  * returns 1 if connection should be dropped, 0 otherwise.
  * dropping starts at connection #max_startups_begin with a probability
@@ -860,17 +338,64 @@ usage(void)
        exit(1);
 }
 
+static struct sshbuf *
+pack_hostkeys(void)
+{
+       struct sshbuf *keybuf = NULL, *hostkeys = NULL;
+       int r;
+       u_int i;
+
+       if ((keybuf = sshbuf_new()) == NULL ||
+           (hostkeys = sshbuf_new()) == NULL)
+               fatal_f("sshbuf_new failed");
+
+       /* pack hostkeys into a string. Empty key slots get empty strings */
+       for (i = 0; i < options.num_host_key_files; i++) {
+               /* private key */
+               sshbuf_reset(keybuf);
+               if (sensitive_data.host_keys[i] != NULL &&
+                   (r = sshkey_private_serialize(sensitive_data.host_keys[i],
+                   keybuf)) != 0)
+                       fatal_fr(r, "serialize hostkey private");
+               if ((r = sshbuf_put_stringb(hostkeys, keybuf)) != 0)
+                       fatal_fr(r, "compose hostkey private");
+               /* public key */
+               if (sensitive_data.host_pubkeys[i] != NULL) {
+                       if ((r = sshkey_puts(sensitive_data.host_pubkeys[i],
+                           hostkeys)) != 0)
+                               fatal_fr(r, "compose hostkey public");
+               } else {
+                       if ((r = sshbuf_put_string(hostkeys, NULL, 0)) != 0)
+                               fatal_fr(r, "compose hostkey empty public");
+               }
+               /* cert */
+               if (sensitive_data.host_certificates[i] != NULL) {
+                       if ((r = sshkey_puts(
+                           sensitive_data.host_certificates[i],
+                           hostkeys)) != 0)
+                               fatal_fr(r, "compose host cert");
+               } else {
+                       if ((r = sshbuf_put_string(hostkeys, NULL, 0)) != 0)
+                               fatal_fr(r, "compose host cert empty");
+               }
+       }
+
+       sshbuf_free(keybuf);
+       return hostkeys;
+}
+
 static void
 send_rexec_state(int fd, struct sshbuf *conf)
 {
-       struct sshbuf *m = NULL, *inc = NULL;
+       struct sshbuf *m = NULL, *inc = NULL, *hostkeys = NULL;
        struct include_item *item = NULL;
-       int r;
+       int r, sz;
 
        debug3_f("entering fd = %d config len %zu", fd,
            sshbuf_len(conf));
 
-       if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL)
+       if ((m = sshbuf_new()) == NULL ||
+           (inc = sshbuf_new()) == NULL)
                fatal_f("sshbuf_new failed");
 
        /* pack includes into a string */
@@ -881,9 +406,17 @@ send_rexec_state(int fd, struct sshbuf *conf)
                        fatal_fr(r, "compose includes");
        }
 
+       hostkeys = pack_hostkeys();
+
        /*
         * Protocol from reexec master to child:
         *      string  configuration
+        *      uint64  timing_secret
+        *      string  host_keys[] {
+        *              string private_key
+        *              string public_key
+        *              string certificate
+        *      }
         *      string  included_files[] {
         *              string  selector
         *              string  filename
@@ -891,81 +424,26 @@ send_rexec_state(int fd, struct sshbuf *conf)
         *      }
         */
        if ((r = sshbuf_put_stringb(m, conf)) != 0 ||
+           (r = sshbuf_put_u64(m, options.timing_secret)) != 0 ||
+           (r = sshbuf_put_stringb(m, hostkeys)) != 0 ||
            (r = sshbuf_put_stringb(m, inc)) != 0)
                fatal_fr(r, "compose config");
+
+       /* We need to fit the entire message inside the socket send buffer */
+       sz = ROUNDUP(sshbuf_len(m) + 5, 16*1024);
+       if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sz, sizeof sz) == -1)
+               fatal_f("setsockopt SO_SNDBUF: %s", strerror(errno));
+
        if (ssh_msg_send(fd, 0, m) == -1)
                error_f("ssh_msg_send failed");
 
        sshbuf_free(m);
        sshbuf_free(inc);
+       sshbuf_free(hostkeys);
 
        debug3_f("done");
 }
 
-static void
-recv_rexec_state(int fd, struct sshbuf *conf)
-{
-       struct sshbuf *m, *inc;
-       u_char *cp, ver;
-       size_t len;
-       int r;
-       struct include_item *item;
-
-       debug3_f("entering fd = %d", fd);
-
-       if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL)
-               fatal_f("sshbuf_new failed");
-       if (ssh_msg_recv(fd, m) == -1)
-               fatal_f("ssh_msg_recv failed");
-       if ((r = sshbuf_get_u8(m, &ver)) != 0)
-               fatal_fr(r, "parse version");
-       if (ver != 0)
-               fatal_f("rexec version mismatch");
-       if ((r = sshbuf_get_string(m, &cp, &len)) != 0 ||
-           (r = sshbuf_get_stringb(m, inc)) != 0)
-               fatal_fr(r, "parse config");
-
-       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);
-
-       debug3_f("done");
-}
-
-/* Accept a connection from inetd */
-static void
-server_accept_inetd(int *sock_in, int *sock_out)
-{
-       if (rexeced_flag) {
-               close(REEXEC_CONFIG_PASS_FD);
-               *sock_in = *sock_out = dup(STDIN_FILENO);
-       } else {
-               *sock_in = dup(STDIN_FILENO);
-               *sock_out = dup(STDOUT_FILENO);
-       }
-       /*
-        * We intentionally do not close the descriptors 0, 1, and 2
-        * as our code for setting the descriptors won't work if
-        * ttyfd happens to be one of those.
-        */
-       if (stdfd_devnull(1, 1, !log_stderr) == -1)
-               error_f("stdfd_devnull failed");
-       debug("inetd sockets after dupping: %d, %d", *sock_in, *sock_out);
-}
-
 /*
  * Listen for TCP connections
  */
@@ -1066,7 +544,8 @@ server_listen(void)
  * from this function are in a forked subprocess.
  */
 static void
-server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
+server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s,
+    int log_stderr)
 {
        struct pollfd *pfd = NULL;
        int i, j, ret, npfd;
@@ -1223,7 +702,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
                                continue;
                        }
 
-                       if (rexec_flag && socketpair(AF_UNIX,
+                       if (socketpair(AF_UNIX,
                            SOCK_STREAM, 0, config_s) == -1) {
                                error("reexec socketpair: %s",
                                    strerror(errno));
@@ -1259,10 +738,8 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
                                close(startup_p[1]);
                                startup_pipe = -1;
                                pid = getpid();
-                               if (rexec_flag) {
-                                       send_rexec_state(config_s[0], cfg);
-                                       close(config_s[0]);
-                               }
+                               send_rexec_state(config_s[0], cfg);
+                               close(config_s[0]);
                                free(pfd);
                                return;
                        }
@@ -1291,19 +768,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
                                    options.log_level,
                                    options.log_facility,
                                    log_stderr);
-                               if (rexec_flag)
-                                       close(config_s[0]);
-                               else {
-                                       /*
-                                        * Signal parent that the preliminaries
-                                        * for this child are complete. For the
-                                        * re-exec case, this happens after the
-                                        * child has received the rexec state
-                                        * from the server.
-                                        */
-                                       (void)atomicio(vwrite, startup_pipe,
-                                           "\0", 1);
-                               }
+                               close(config_s[0]);
                                free(pfd);
                                return;
                        }
@@ -1316,81 +781,14 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
 
                        close(startup_p[1]);
 
-                       if (rexec_flag) {
-                               close(config_s[1]);
-                               send_rexec_state(config_s[0], cfg);
-                               close(config_s[0]);
-                       }
+                       close(config_s[1]);
+                       send_rexec_state(config_s[0], cfg);
+                       close(config_s[0]);
                        close(*newsock);
                }
        }
 }
 
-/*
- * If IP options are supported, make sure there are none (log and
- * return an error if any are found).  Basically we are worried about
- * source routing; it can be used to pretend you are somebody
- * (ip-address) you are not. That itself may be "almost acceptable"
- * under certain circumstances, but rhosts authentication is useless
- * if source routing is accepted. Notice also that if we just dropped
- * source routing here, the other side could use IP spoofing to do
- * rest of the interaction and could still bypass security.  So we
- * exit here if we detect any IP options.
- */
-static void
-check_ip_options(struct ssh *ssh)
-{
-       int sock_in = ssh_packet_get_connection_in(ssh);
-       struct sockaddr_storage from;
-       u_char opts[200];
-       socklen_t i, option_size = sizeof(opts), fromlen = sizeof(from);
-       char text[sizeof(opts) * 3 + 1];
-
-       memset(&from, 0, sizeof(from));
-       if (getpeername(sock_in, (struct sockaddr *)&from,
-           &fromlen) == -1)
-               return;
-       if (from.ss_family != AF_INET)
-               return;
-       /* XXX IPv6 options? */
-
-       if (getsockopt(sock_in, IPPROTO_IP, IP_OPTIONS, opts,
-           &option_size) >= 0 && option_size != 0) {
-               text[0] = '\0';
-               for (i = 0; i < option_size; i++)
-                       snprintf(text + i*3, sizeof(text) - i*3,
-                           " %2.2x", opts[i]);
-               fatal("Connection from %.100s port %d with IP opts: %.800s",
-                   ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), text);
-       }
-       return;
-}
-
-/* Set the routing domain for this process */
-static void
-set_process_rdomain(struct ssh *ssh, const char *name)
-{
-       int rtable, ortable = getrtable();
-       const char *errstr;
-
-       if (name == NULL)
-               return; /* default */
-
-       if (strcmp(name, "%D") == 0) {
-               /* "expands" to routing domain of connection */
-               if ((name = ssh_packet_rdomain_in(ssh)) == NULL)
-                       return;
-       }
-
-       rtable = (int)strtonum(name, 0, 255, &errstr);
-       if (errstr != NULL) /* Shouldn't happen */
-               fatal("Invalid routing domain \"%s\": %s", name, errstr);
-       if (rtable != ortable && setrtable(rtable) != 0)
-               fatal("Unable to set routing domain %d: %s",
-                   rtable, strerror(errno));
-       debug_f("set routing domain %d (was %d)", rtable, ortable);
-}
-
 static void
 accumulate_host_timing_secret(struct sshbuf *server_cfg,
     struct sshkey *key)
@@ -1440,14 +838,8 @@ prepare_proctitle(int ac, char **av)
 }
 
 static void
-print_config(struct ssh *ssh, struct connection_info *connection_info)
+print_config(struct connection_info *connection_info)
 {
-       /*
-        * If no connection info was provided by -C then use
-        * use a blank one that will cause no predicate to match.
-        */
-       if (connection_info == NULL)
-               connection_info = get_connection_info(ssh, 0, 0);
        connection_info->test = 1;
        parse_server_match_config(&options, &includes, connection_info);
        dump_config(&options);
@@ -1460,24 +852,24 @@ print_config(struct ssh *ssh, struct connection_info *connection_info)
 int
 main(int ac, char **av)
 {
-       struct ssh *ssh = NULL;
        extern char *optarg;
        extern int optind;
-       int r, opt, on = 1, do_dump_cfg = 0, already_daemon, remote_port;
-       int sock_in = -1, sock_out = -1, newsock = -1;
-       const char *remote_ip, *rdomain;
-       char *fp, *line, *laddr, *logfile = NULL;
-       int config_s[2] = { -1 , -1 };
+       int log_stderr = 0, inetd_flag = 0, test_flag = 0, no_daemon_flag = 0;
+       char *config_file_name = _PATH_SERVER_CONFIG_FILE;
+       int r, opt, do_dump_cfg = 0, keytype, already_daemon, have_agent = 0;
+       int sock_in = -1, sock_out = -1, newsock = -1, rexec_argc = 0;
+       int config_s[2] = { -1 , -1 }, have_connection_info = 0;
+       char *fp, *line, *logfile = NULL, **rexec_argv = NULL;
+       struct stat sb;
        u_int i, j;
-       u_int64_t ibytes, obytes;
        mode_t new_umask;
        struct sshkey *key;
        struct sshkey *pubkey;
-       int keytype;
-       Authctxt *authctxt;
-       struct connection_info *connection_info = NULL;
+       struct connection_info connection_info;
        sigset_t sigmask;
 
+       memset(&connection_info, 0, sizeof(connection_info));
+
        sigemptyset(&sigmask);
        sigprocmask(SIG_SETMASK, &sigmask, NULL);
 
@@ -1531,11 +923,10 @@ main(int ac, char **av)
                        inetd_flag = 1;
                        break;
                case 'r':
-                       rexec_flag = 0;
+                       /* ignored */
                        break;
                case 'R':
-                       rexeced_flag = 1;
-                       inetd_flag = 1;
+                       fatal("-R not supported here");
                        break;
                case 'Q':
                        /* ignored */
@@ -1578,10 +969,10 @@ main(int ac, char **av)
                        test_flag = 2;
                        break;
                case 'C':
-                       connection_info = get_connection_info(ssh, 0, 0);
-                       if (parse_server_match_testspec(connection_info,
+                       if (parse_server_match_testspec(&connection_info,
                            optarg) == -1)
                                exit(1);
+                       have_connection_info = 1;
                        break;
                case 'u':
                        utmp_len = (u_int)strtonum(optarg, 0, HOST_NAME_MAX+1+1, NULL);
@@ -1606,22 +997,27 @@ main(int ac, char **av)
                        break;
                }
        }
-       if (rexeced_flag || inetd_flag)
-               rexec_flag = 0;
-       if (!test_flag && !do_dump_cfg && rexec_flag && !path_absolute(av[0]))
-               fatal("sshd re-exec requires execution with an absolute path");
-       if (rexeced_flag)
-               closefrom(REEXEC_MIN_FREE_FD);
-       else
-               closefrom(REEXEC_DEVCRYPTO_RESERVED_FD);
+       if (!test_flag && !do_dump_cfg && !path_absolute(av[0]))
+               fatal("sshd requires execution with an absolute path");
+       closefrom(REEXEC_DEVCRYPTO_RESERVED_FD);
 
 #ifdef WITH_OPENSSL
        OpenSSL_add_all_algorithms();
 #endif
 
        /* If requested, redirect the logs to the specified logfile. */
-       if (logfile != NULL)
-               log_redirect_stderr_to(logfile);
+       if (logfile != NULL) {
+               char *cp, pid_s[32];
+
+               snprintf(pid_s, sizeof(pid_s), "%ld", (unsigned long)getpid());
+               cp = percent_expand(logfile,
+                   "p", pid_s,
+                   "P", "sshd",
+                   (char *)NULL);
+               log_redirect_stderr_to(cp);
+               free(cp);
+       }
+
        /*
         * Force logging to stderr until we have loaded the private host
         * key (unless started from inetd)
@@ -1639,35 +1035,18 @@ main(int ac, char **av)
         * If we're not doing an extended test do not silently ignore connection
         * test params.
         */
-       if (test_flag < 2 && connection_info != NULL)
+       if (test_flag < 2 && have_connection_info)
                fatal("Config test connection parameter (-C) provided without "
                    "test mode (-T)");
 
        /* Fetch our configuration */
        if ((cfg = sshbuf_new()) == NULL)
-               fatal_f("sshbuf_new failed");
-       if (rexeced_flag) {
-               setproctitle("%s", "[rexeced]");
-               recv_rexec_state(REEXEC_CONFIG_PASS_FD, cfg);
-               if (!debug_flag) {
-                       startup_pipe = dup(REEXEC_STARTUP_PIPE_FD);
-                       close(REEXEC_STARTUP_PIPE_FD);
-                       /*
-                        * Signal parent that this child is at a point where
-                        * they can go away if they have a SIGHUP pending.
-                        */
-                       (void)atomicio(vwrite, startup_pipe, "\0", 1);
-               }
-       } else if (strcasecmp(config_file_name, "none") != 0)
+               fatal("sshbuf_new config failed");
+       if (strcasecmp(config_file_name, "none") != 0)
                load_server_config(config_file_name, cfg);
 
-       parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name,
-           cfg, &includes, NULL, rexeced_flag);
-
-#ifdef WITH_OPENSSL
-       if (options.moduli_file != NULL)
-               dh_set_moduli_file(options.moduli_file);
-#endif
+       parse_server_config(&options, config_file_name, cfg,
+           &includes, NULL, 0);
 
        /* Fill in default values for those options not explicitly set. */
        fill_default_server_options(&options);
@@ -1687,7 +1066,7 @@ main(int ac, char **av)
        /*
         * Check whether there is any path through configured auth methods.
         * Unfortunately it is not possible to verify this generally before
-        * daemonisation in the presence of Match block, but this catches
+        * daemonisation in the presence of Match blocks, but this catches
         * and warns for trivial misconfigurations that could break login.
         */
        if (options.num_auth_methods != 0) {
@@ -1710,7 +1089,7 @@ main(int ac, char **av)
        debug("sshd version %s, %s", SSH_VERSION, SSH_OPENSSL_VERSION);
 
        if (do_dump_cfg)
-               print_config(ssh, connection_info);
+               print_config(&connection_info);
 
        /* load host keys */
        sensitive_data.host_keys = xcalloc(options.num_host_key_files,
@@ -1862,40 +1241,42 @@ main(int ac, char **av)
                    sshkey_type(key));
        }
 
-       if (use_privsep) {
-               struct stat st;
-
-               if (getpwnam(SSH_PRIVSEP_USER) == NULL)
-                       fatal("Privilege separation user %s does not exist",
-                           SSH_PRIVSEP_USER);
-               endpwent();
-               if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) ||
-                   (S_ISDIR(st.st_mode) == 0))
-                       fatal("Missing privilege separation directory: %s",
-                           _PATH_PRIVSEP_CHROOT_DIR);
-               if (st.st_uid != 0 || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0)
-                       fatal("%s must be owned by root and not group or "
-                           "world-writable.", _PATH_PRIVSEP_CHROOT_DIR);
-       }
+       /* Ensure privsep directory is correctly configured. */
+       if (getpwnam(SSH_PRIVSEP_USER) == NULL)
+               fatal("Privilege separation user %s does not exist",
+                   SSH_PRIVSEP_USER);
+       endpwent();
+       if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &sb) == -1) ||
+           (S_ISDIR(sb.st_mode) == 0))
+               fatal("Missing privilege separation directory: %s",
+                   _PATH_PRIVSEP_CHROOT_DIR);
+       if (sb.st_uid != 0 || (sb.st_mode & (S_IWGRP|S_IWOTH)) != 0)
+               fatal("%s must be owned by root and not group or "
+                   "world-writable.", _PATH_PRIVSEP_CHROOT_DIR);
 
        if (test_flag > 1)
-               print_config(ssh, connection_info);
+               print_config(&connection_info);
 
        /* Configuration looks good, so exit if in test mode. */
        if (test_flag)
                exit(0);
 
-       if (rexec_flag) {
-               if (rexec_argc < 0)
-                       fatal("rexec_argc %d < 0", rexec_argc);
-               rexec_argv = xcalloc(rexec_argc + 2, sizeof(char *));
-               for (i = 0; i < (u_int)rexec_argc; i++) {
-                       debug("rexec_argv[%d]='%s'", i, saved_argv[i]);
-                       rexec_argv[i] = saved_argv[i];
-               }
-               rexec_argv[rexec_argc] = "-R";
-               rexec_argv[rexec_argc + 1] = NULL;
+       /* Prepare arguments for sshd-session */
+       if (rexec_argc < 0)
+               fatal("rexec_argc %d < 0", rexec_argc);
+       rexec_argv = xcalloc(rexec_argc + 3, sizeof(char *));
+       /* Point to the sshd-session binary instead of sshd */
+       rexec_argv[0] = options.sshd_session_path;
+       for (i = 1; i < (u_int)rexec_argc; i++) {
+               debug("rexec_argv[%d]='%s'", i, saved_argv[i]);
+               rexec_argv[i] = saved_argv[i];
        }
+       rexec_argv[rexec_argc++] = "-R";
+       rexec_argv[rexec_argc] = NULL;
+       if (stat(rexec_argv[0], &sb) != 0 || !(sb.st_mode & (S_IXOTH|S_IXUSR)))
+               fatal("%s does not exist or is not executable", rexec_argv[0]);
+       debug3("using %s for re-exec", rexec_argv[0]);
+
        listener_proctitle = prepare_proctitle(ac, av);
 
        /* Ensure that umask disallows at least group and world write */
@@ -1903,7 +1284,7 @@ main(int ac, char **av)
        (void) umask(new_umask);
 
        /* Initialize the log (it is reinitialized below in case we forked). */
-       if (debug_flag && (!inetd_flag || rexeced_flag))
+       if (debug_flag && !inetd_flag)
                log_stderr = 1;
        log_init(__progname, options.log_level,
            options.log_facility, log_stderr);
@@ -1938,7 +1319,11 @@ main(int ac, char **av)
 
        /* Get a connection, either from inetd or a listening TCP socket */
        if (inetd_flag) {
-               server_accept_inetd(&sock_in, &sock_out);
+               /* Send configuration to ancestor sshd-session process */
+               if (socketpair(AF_UNIX, SOCK_STREAM, 0, config_s) == -1)
+                       fatal("socketpair: %s", strerror(errno));
+               send_rexec_state(config_s[0], cfg);
+               close(config_s[0]);
        } else {
                server_listen();
 
@@ -1965,7 +1350,7 @@ main(int ac, char **av)
 
                /* Accept a connection and return in a forked child */
                server_accept_loop(&sock_in, &sock_out,
-                   &newsock, config_s);
+                   &newsock, config_s, log_stderr);
        }
 
        /* This is the child processing a new connection. */
@@ -1979,316 +1364,36 @@ main(int ac, char **av)
        if (!debug_flag && !inetd_flag && setsid() == -1)
                error("setsid: %.100s", strerror(errno));
 
-       if (rexec_flag) {
-               debug("rexec start in %d out %d newsock %d pipe %d sock %d",
-                   sock_in, sock_out, newsock, startup_pipe, config_s[0]);
+       debug("rexec start in %d out %d newsock %d pipe %d sock %d/%d",
+           sock_in, sock_out, newsock, startup_pipe, config_s[0], config_s[1]);
+       if (!inetd_flag) {
                if (dup2(newsock, STDIN_FILENO) == -1)
-                       debug3_f("dup2 stdin: %s", strerror(errno));
+                       debug3("dup2 stdin: %s", strerror(errno));
                if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1)
-                       debug3_f("dup2 stdout: %s", strerror(errno));
-               if (startup_pipe == -1)
-                       close(REEXEC_STARTUP_PIPE_FD);
-               else if (startup_pipe != REEXEC_STARTUP_PIPE_FD) {
-                       if (dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD) == -1)
-                               debug3_f("dup2 startup_p: %s", strerror(errno));
-                       close(startup_pipe);
-                       startup_pipe = REEXEC_STARTUP_PIPE_FD;
-               }
-
+                       debug3("dup2 stdout: %s", strerror(errno));
+       }
+       if (config_s[1] != REEXEC_CONFIG_PASS_FD) {
                if (dup2(config_s[1], REEXEC_CONFIG_PASS_FD) == -1)
-                       debug3_f("dup2 config_s: %s", strerror(errno));
+                       debug3("dup2 config_s: %s", strerror(errno));
                close(config_s[1]);
-
-               ssh_signal(SIGHUP, SIG_IGN); /* avoid reset to SIG_DFL */
-               execv(rexec_argv[0], rexec_argv);
-
-               /* Reexec has failed, fall back and continue */
-               error("rexec of %s failed: %s", rexec_argv[0], strerror(errno));
-               recv_rexec_state(REEXEC_CONFIG_PASS_FD, NULL);
-               log_init(__progname, options.log_level,
-                   options.log_facility, log_stderr);
-
-               /* Clean up fds */
-               close(REEXEC_CONFIG_PASS_FD);
-               newsock = sock_out = sock_in = dup(STDIN_FILENO);
-               if (stdfd_devnull(1, 1, 0) == -1)
-                       error_f("stdfd_devnull failed");
-               debug("rexec cleanup in %d out %d newsock %d pipe %d sock %d",
-                   sock_in, sock_out, newsock, startup_pipe, config_s[0]);
-       }
-
-       /* Executed child processes don't need these. */
-       fcntl(sock_out, F_SETFD, FD_CLOEXEC);
-       fcntl(sock_in, F_SETFD, FD_CLOEXEC);
-
-       /* We will not restart on SIGHUP since it no longer makes sense. */
-       ssh_signal(SIGALRM, SIG_DFL);
-       ssh_signal(SIGHUP, SIG_DFL);
-       ssh_signal(SIGTERM, SIG_DFL);
-       ssh_signal(SIGQUIT, SIG_DFL);
-       ssh_signal(SIGCHLD, SIG_DFL);
-
-       /*
-        * Register our connection.  This turns encryption off because we do
-        * not have a key.
-        */
-       if ((ssh = ssh_packet_set_connection(NULL, sock_in, sock_out)) == NULL)
-               fatal("Unable to create connection");
-       the_active_state = ssh;
-       ssh_packet_set_server(ssh);
-
-       check_ip_options(ssh);
-
-       /* Prepare the channels layer */
-       channel_init_channels(ssh);
-       channel_set_af(ssh, options.address_family);
-       process_channel_timeouts(ssh, &options);
-       process_permitopen(ssh, &options);
-
-       /* Set SO_KEEPALIVE if requested. */
-       if (options.tcp_keep_alive && ssh_packet_connection_is_on_socket(ssh) &&
-           setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1)
-               error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
-
-       if ((remote_port = ssh_remote_port(ssh)) < 0) {
-               debug("ssh_remote_port failed");
-               cleanup_exit(255);
-       }
-
-       /*
-        * The rest of the code depends on the fact that
-        * ssh_remote_ipaddr() caches the remote ip, even if
-        * the socket goes away.
-        */
-       remote_ip = ssh_remote_ipaddr(ssh);
-
-       rdomain = ssh_packet_rdomain_in(ssh);
-
-       /* Log the connection. */
-       laddr = get_local_ipaddr(sock_in);
-       verbose("Connection from %s port %d on %s port %d%s%s%s",
-           remote_ip, remote_port, laddr,  ssh_local_port(ssh),
-           rdomain == NULL ? "" : " rdomain \"",
-           rdomain == NULL ? "" : rdomain,
-           rdomain == NULL ? "" : "\"");
-       free(laddr);
-
-       /*
-        * We don't want to listen forever unless the other side
-        * successfully authenticates itself.  So we set up an alarm which is
-        * cleared after successful authentication.  A limit of zero
-        * indicates no limit. Note that we don't set the alarm in debugging
-        * mode; it is just annoying to have the server exit just when you
-        * are about to discover the bug.
-        */
-       ssh_signal(SIGALRM, grace_alarm_handler);
-       if (!debug_flag)
-               alarm(options.login_grace_time);
-
-       if ((r = kex_exchange_identification(ssh, -1,
-           options.version_addendum)) != 0)
-               sshpkt_fatal(ssh, r, "banner exchange");
-
-       ssh_packet_set_nonblocking(ssh);
-
-       /* allocate authentication context */
-       authctxt = xcalloc(1, sizeof(*authctxt));
-       ssh->authctxt = authctxt;
-
-       /* XXX global for cleanup, access from other modules */
-       the_authctxt = authctxt;
-
-       /* Set default key authentication options */
-       if ((auth_opts = sshauthopt_new_with_keys_defaults()) == NULL)
-               fatal("allocation failed");
-
-       /* prepare buffer to collect messages to display to user after login */
-       if ((loginmsg = sshbuf_new()) == NULL)
-               fatal_f("sshbuf_new failed");
-       auth_debug_reset();
-
-       if (use_privsep) {
-               if (privsep_preauth(ssh) == 1)
-                       goto authenticated;
-       } else if (have_agent) {
-               if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) {
-                       error_r(r, "Unable to get agent socket");
-                       have_agent = 0;
-               }
        }
-
-       /* perform the key exchange */
-       /* authenticate user and start session */
-       do_ssh2_kex(ssh);
-       do_authentication2(ssh);
-
-       /*
-        * If we use privilege separation, the unprivileged child transfers
-        * the current keystate and exits
-        */
-       if (use_privsep) {
-               mm_send_keystate(ssh, pmonitor);
-               ssh_packet_clear_keys(ssh);
-               exit(0);
-       }
-
- authenticated:
-       /*
-        * Cancel the alarm we set to limit the time taken for
-        * authentication.
-        */
-       alarm(0);
-       ssh_signal(SIGALRM, SIG_DFL);
-       authctxt->authenticated = 1;
-       if (startup_pipe != -1) {
+       if (startup_pipe == -1)
+               close(REEXEC_STARTUP_PIPE_FD);
+       else if (startup_pipe != REEXEC_STARTUP_PIPE_FD) {
+               if (dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD) == -1)
+                       debug3("dup2 startup_p: %s", strerror(errno));
                close(startup_pipe);
-               startup_pipe = -1;
-       }
-
-       if (options.routing_domain != NULL)
-               set_process_rdomain(ssh, options.routing_domain);
-
-       /*
-        * In privilege separation, we fork another child and prepare
-        * file descriptor passing.
-        */
-       if (use_privsep) {
-               privsep_postauth(ssh, authctxt);
-               /* the monitor process [priv] will not return */
-       }
-
-       ssh_packet_set_timeout(ssh, options.client_alive_interval,
-           options.client_alive_count_max);
-
-       /* Try to send all our hostkeys to the client */
-       notify_hostkeys(ssh);
-
-       /* Start session. */
-       do_authenticated(ssh, authctxt);
-
-       /* The connection has been terminated. */
-       ssh_packet_get_bytes(ssh, &ibytes, &obytes);
-       verbose("Transferred: sent %llu, received %llu bytes",
-           (unsigned long long)obytes, (unsigned long long)ibytes);
-
-       verbose("Closing connection to %.500s port %d", remote_ip, remote_port);
-       ssh_packet_close(ssh);
-
-       if (use_privsep)
-               mm_terminate();
-
-       exit(0);
-}
-
-int
-sshd_hostkey_sign(struct ssh *ssh, struct sshkey *privkey,
-    struct sshkey *pubkey, u_char **signature, size_t *slenp,
-    const u_char *data, size_t dlen, const char *alg)
-{
-       int r;
-
-       if (use_privsep) {
-               if (privkey) {
-                       if (mm_sshkey_sign(ssh, privkey, signature, slenp,
-                           data, dlen, alg, options.sk_provider, NULL,
-                           ssh->compat) < 0)
-                               fatal_f("privkey sign failed");
-               } else {
-                       if (mm_sshkey_sign(ssh, pubkey, signature, slenp,
-                           data, dlen, alg, options.sk_provider, NULL,
-                           ssh->compat) < 0)
-                               fatal_f("pubkey sign failed");
-               }
-       } else {
-               if (privkey) {
-                       if (sshkey_sign(privkey, signature, slenp, data, dlen,
-                           alg, options.sk_provider, NULL, ssh->compat) < 0)
-                               fatal_f("privkey sign failed");
-               } else {
-                       if ((r = ssh_agent_sign(auth_sock, pubkey,
-                           signature, slenp, data, dlen, alg,
-                           ssh->compat)) != 0) {
-                               fatal_fr(r, "agent sign failed");
-                       }
-               }
        }
-       return 0;
-}
-
-/* SSH2 key exchange */
-static void
-do_ssh2_kex(struct ssh *ssh)
-{
-       char *hkalgs = NULL, *myproposal[PROPOSAL_MAX];
-       const char *compression = NULL;
-       struct kex *kex;
-       int r;
 
-       if (options.rekey_limit || options.rekey_interval)
-               ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
-                   options.rekey_interval);
+       ssh_signal(SIGHUP, SIG_IGN); /* avoid reset to SIG_DFL */
+       execv(rexec_argv[0], rexec_argv);
 
-       if (options.compression == COMP_NONE)
-               compression = "none";
-       hkalgs = list_hostkey_types();
-
-       kex_proposal_populate_entries(ssh, myproposal, options.kex_algorithms,
-           options.ciphers, options.macs, compression, hkalgs);
-
-       free(hkalgs);
-
-       /* start key exchange */
-       if ((r = kex_setup(ssh, myproposal)) != 0)
-               fatal_r(r, "kex_setup");
-       kex_set_server_sig_algs(ssh, options.pubkey_accepted_algos);
-       kex = ssh->kex;
-
-#ifdef WITH_OPENSSL
-       kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_server;
-       kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_server;
-       kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_server;
-       kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_server;
-       kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_server;
-       kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
-       kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
-       kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
-#endif
-       kex->kex[KEX_C25519_SHA256] = kex_gen_server;
-       kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
-       kex->load_host_public_key=&get_hostkey_public_by_type;
-       kex->load_host_private_key=&get_hostkey_private_by_type;
-       kex->host_key_index=&get_hostkey_index;
-       kex->sign = sshd_hostkey_sign;
-
-       ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &kex->done);
-       kex_proposal_free_entries(myproposal);
-
-#ifdef DEBUG_KEXDH
-       /* send 1st encrypted/maced/compressed message */
-       if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 ||
-           (r = sshpkt_put_cstring(ssh, "markus")) != 0 ||
-           (r = sshpkt_send(ssh)) != 0 ||
-           (r = ssh_packet_write_wait(ssh)) != 0)
-               fatal_fr(r, "send test");
-#endif
-       debug("KEX done");
+       fatal("rexec of %s failed: %s", rexec_argv[0], strerror(errno));
 }
 
 /* server specific fatal cleanup */
 void
 cleanup_exit(int i)
 {
-       if (the_active_state != NULL && the_authctxt != NULL) {
-               do_cleanup(the_active_state, the_authctxt);
-               if (use_privsep && privsep_is_preauth &&
-                   pmonitor != NULL && pmonitor->m_pid > 1) {
-                       debug("Killing privsep child %d", pmonitor->m_pid);
-                       if (kill(pmonitor->m_pid, SIGKILL) != 0 &&
-                           errno != ESRCH) {
-                               error_f("kill(%d): %s", pmonitor->m_pid,
-                                   strerror(errno));
-                       }
-               }
-       }
        _exit(i);
 }
index 77a0b8d..6543436 100644 (file)
@@ -1,17 +1,11 @@
-#      $OpenBSD: Makefile,v 1.110 2024/04/01 15:48:16 deraadt Exp $
+#      $OpenBSD: Makefile,v 1.111 2024/05/17 00:30:24 djm Exp $
 
 .PATH:         ${.CURDIR}/..
 
-SRCS=  sshd.c auth-rhosts.c auth-passwd.c sshpty.c sshlogin.c servconf.c \
-       serverloop.c auth.c auth2.c auth-options.c session.c auth2-chall.c \
-       groupaccess.c auth-bsdauth.c auth2-hostbased.c auth2-kbdint.c \
-       auth2-none.c auth2-passwd.c auth2-pubkey.c auth2-pubkeyfile.c \
-       monitor.c monitor_wrap.c \
-       sftp-server.c sftp-common.c sftp-realpath.c sandbox-pledge.c srclimit.c
-SRCS+= authfd.c compat.c dns.c fatal.c hostfile.c readpass.c utf8.c uidswap.c
-SRCS+= ${SRCS_BASE} ${SRCS_KEX} ${SRCS_KEXS} ${SRCS_KEY} ${SRCS_KEYP} \
-       ${SRCS_KRL} ${SRCS_PROT} ${SRCS_PKT} ${SRCS_UTL} ${SRCS_PKCS11} \
-       ${SRCS_SK_CLIENT}
+SRCS=  sshd.c servconf.c sshpty.c srclimit.c groupaccess.c auth2-methods.c
+SRCS+= dns.c fatal.c compat.c utf8.c authfd.c canohost.c kex-names.c
+SRCS+= ${SRCS_BASE} ${SRCS_KEY} ${SRCS_KEYP} ${SRCS_KRL} \
+       ${SRCS_MAC} ${SRCS_UTL} ${SRCS_SK_CLIENT}
 
 PROG=  sshd
 BINMODE=511
@@ -50,7 +44,7 @@ DPADD+=       ${LIBZ}
 
 # The random relink kit, used on OpenBSD by /etc/rc
 
-CLEANFILES+= sshd.tar install.sh
+CLEANFILES+= ${PROG}.tar install.sh
 
 install.sh: Makefile
        echo "set -o errexit" > $@