diff options
Diffstat (limited to 'crypto/openssh/sshd-session.c')
-rw-r--r-- | crypto/openssh/sshd-session.c | 334 |
1 files changed, 121 insertions, 213 deletions
diff --git a/crypto/openssh/sshd-session.c b/crypto/openssh/sshd-session.c index ee983176d3aa..902718524279 100644 --- a/crypto/openssh/sshd-session.c +++ b/crypto/openssh/sshd-session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd-session.c,v 1.9 2024/09/09 02:39:57 djm Exp $ */ +/* $OpenBSD: sshd-session.c,v 1.12 2025/03/12 22:43:44 djm Exp $ */ /* * SSH2 implementation: * Privilege Separation: @@ -102,7 +102,6 @@ #include "ssh-gss.h" #endif #include "monitor_wrap.h" -#include "ssh-sandbox.h" #include "auth-options.h" #include "version.h" #include "ssherr.h" @@ -113,9 +112,13 @@ /* 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) +#define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 2) +#define REEXEC_MIN_FREE_FD (STDERR_FILENO + 3) + +/* Privsep fds */ +#define PRIVSEP_MONITOR_FD (STDERR_FILENO + 1) +#define PRIVSEP_LOG_FD (STDERR_FILENO + 2) +#define PRIVSEP_MIN_FREE_FD (STDERR_FILENO + 3) extern char *__progname; @@ -194,7 +197,17 @@ 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 *); + +/* XXX reduce to stub once postauth split */ +int +mm_is_monitor(void) +{ + /* + * m_pid is only set in the privileged part, and + * points to the unprivileged child. + */ + return (pmonitor && pmonitor->m_pid > 0); +} /* * Signal handler for the alarm after the login grace period has expired. @@ -284,41 +297,41 @@ reseed_prngs(void) explicit_bzero(rnd, sizeof(rnd)); } -static void -privsep_preauth_child(void) +struct sshbuf * +pack_hostkeys(void) { - gid_t gidset[1]; - - /* 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 - - reseed_prngs(); + struct sshbuf *keybuf = NULL, *hostkeys = NULL; + int r; + u_int i; - /* Demote the private keys to public keys. */ - demote_sensitive_data(); + if ((hostkeys = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); - /* Demote the child */ - if (privsep_chroot) { - /* 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 */ - debug3("privsep user:group %u:%u", (u_int)privsep_pw->pw_uid, - (u_int)privsep_pw->pw_gid); - gidset[0] = privsep_pw->pw_gid; - if (setgroups(1, gidset) == -1) - fatal("setgroups: %.100s", strerror(errno)); - permanently_set_uid(privsep_pw); + /* pack hostkeys into a string. Empty key slots get empty strings */ + for (i = 0; i < options.num_host_key_files; i++) { + /* 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 int @@ -326,18 +339,15 @@ 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(pmonitor); - pid = fork(); - if (pid == -1) { + if ((pid = fork()) == -1) fatal("fork of unprivileged child failed"); - } else if (pid != 0) { + else if (pid != 0) { debug2("Network child is on pid %ld", (long)pid); pmonitor->m_pid = pid; @@ -348,8 +358,6 @@ privsep_preauth(struct ssh *ssh) have_agent = 0; } } - if (box != NULL) - ssh_sandbox_parent_preauth(box, pid); monitor_child_preauth(ssh, pmonitor); /* Wait for the child's exit status */ @@ -368,23 +376,46 @@ privsep_preauth(struct ssh *ssh) } 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); + /* + * Arrange unpriv-preauth child process fds: + * 0, 1 network socket + * 2 optional stderr + * 3 reserved + * 4 monitor message socket + * 5 monitor logging socket + * + * We know that the monitor sockets will have fds > 4 because + * of the reserved fds in main() + */ - return 0; + if (ssh_packet_get_connection_in(ssh) != STDIN_FILENO && + dup2(ssh_packet_get_connection_in(ssh), STDIN_FILENO) == -1) + fatal("dup2 stdin failed: %s", strerror(errno)); + if (ssh_packet_get_connection_out(ssh) != STDOUT_FILENO && + dup2(ssh_packet_get_connection_out(ssh), + STDOUT_FILENO) == -1) + fatal("dup2 stdout failed: %s", strerror(errno)); + /* leave stderr as-is */ + log_redirect_stderr_to(NULL); /* dup can clobber log fd */ + if (pmonitor->m_recvfd != PRIVSEP_MONITOR_FD && + dup2(pmonitor->m_recvfd, PRIVSEP_MONITOR_FD) == -1) + fatal("dup2 monitor fd: %s", strerror(errno)); + if (pmonitor->m_log_sendfd != PRIVSEP_LOG_FD && + dup2(pmonitor->m_log_sendfd, PRIVSEP_LOG_FD) == -1) + fatal("dup2 log fd: %s", strerror(errno)); + closefrom(PRIVSEP_MIN_FREE_FD); + + saved_argv[0] = options.sshd_auth_path; + execv(options.sshd_auth_path, saved_argv); + + fatal_f("exec of %s failed: %s", + options.sshd_auth_path, strerror(errno)); } } @@ -446,79 +477,6 @@ privsep_postauth(struct ssh *ssh, Authctxt *authctxt) 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) { @@ -746,6 +704,8 @@ recv_rexec_state(int fd, struct sshbuf *conf, uint64_t *timing_secretp) if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); + + /* receive config */ if (ssh_msg_recv(fd, m) == -1) fatal_f("ssh_msg_recv failed"); if ((r = sshbuf_get_u8(m, &ver)) != 0) @@ -754,7 +714,6 @@ recv_rexec_state(int fd, struct sshbuf *conf, uint64_t *timing_secretp) 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"); @@ -772,6 +731,13 @@ recv_rexec_state(int fd, struct sshbuf *conf, uint64_t *timing_secretp) TAILQ_INSERT_TAIL(&includes, item, entry); } + /* receive hostkeys */ + sshbuf_reset(m); + if (ssh_msg_recv(fd, m) == -1) + fatal_f("ssh_msg_recv failed"); + if ((r = sshbuf_get_u8(m, NULL)) != 0 || + (r = sshbuf_froms(m, &hostkeys)) != 0) + fatal_fr(r, "parse config"); parse_hostkeys(hostkeys); free(cp); @@ -872,7 +838,7 @@ main(int ac, char **av) struct ssh *ssh = NULL; extern char *optarg; extern int optind; - int r, opt, on = 1, remote_port; + int devnull, 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; @@ -1029,13 +995,21 @@ main(int ac, char **av) 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); + platform_pre_session_start(); + + /* Reserve fds we'll need later for reexec things */ + if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) + fatal("open %s: %s", _PATH_DEVNULL, strerror(errno)); + while (devnull < PRIVSEP_MIN_FREE_FD) { + if ((devnull = dup(devnull)) == -1) + fatal("dup %s: %s", _PATH_DEVNULL, strerror(errno)); + } + seed_rng(); /* If requested, redirect the logs to the specified logfile. */ @@ -1062,19 +1036,22 @@ main(int ac, char **av) 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; + /* Reinit logging in case config set Level, Facility or Verbose. */ + log_init(__progname, options.log_level, options.log_facility, + log_stderr || !inetd_flag || debug_flag); + + debug("sshd-session version %s, %s", SSH_VERSION, SSH_OPENSSL_VERSION); + /* Store privilege separation user for later use if required. */ privsep_chroot = (getuid() == 0 || geteuid() == 0); if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) { @@ -1088,15 +1065,19 @@ main(int ac, char **av) } endpwent(); - if (!debug_flag) { - startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); - close(REEXEC_STARTUP_PIPE_FD); + if (!debug_flag && !inetd_flag) { + if ((startup_pipe = dup(REEXEC_CONFIG_PASS_FD)) == -1) + fatal("internal error: no startup pipe"); + /* * 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); } + /* close the fd, but keep the slot reserved */ + if (dup2(devnull, REEXEC_CONFIG_PASS_FD) == -1) + fatal("dup2 devnull->config fd: %s", strerror(errno)); /* Check that options are sensible */ if (options.authorized_keys_command_user == NULL && @@ -1319,22 +1300,11 @@ main(int ac, char **av) BLACKLIST_INIT(); - if (privsep_preauth(ssh) == 1) - goto authenticated; - - /* perform the key exchange */ - /* authenticate user and start session */ - do_ssh2_kex(ssh); - do_authentication2(ssh); + if (privsep_preauth(ssh) != 1) + fatal("privsep_preauth failed"); - /* - * The unprivileged child now transfers the current keystate and exits. - */ - mm_send_keystate(ssh, pmonitor); - ssh_packet_clear_keys(ssh); - exit(0); + /* Now user is authenticated */ - authenticated: /* * Cancel the alarm we set to limit the time taken for * authentication. @@ -1431,68 +1401,6 @@ sshd_hostkey_sign(struct ssh *ssh, struct sshkey *privkey, 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; - #ifdef OPENSSL_HAS_ECC - kex->kex[KEX_ECDH_SHA2] = kex_gen_server; - #endif -#endif - kex->kex[KEX_C25519_SHA256] = kex_gen_server; - kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server; - kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = 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) |