summaryrefslogtreecommitdiff
path: root/sshd.c
diff options
context:
space:
mode:
Diffstat (limited to 'sshd.c')
-rw-r--r--sshd.c1816
1 files changed, 794 insertions, 1022 deletions
diff --git a/sshd.c b/sshd.c
index 54ea6c06..fba25e6b 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd.c,v 1.444 2015/02/20 22:17:21 djm Exp $ */
+/* $OpenBSD: sshd.c,v 1.552 2020/03/13 04:01:57 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -87,20 +87,19 @@
#include "xmalloc.h"
#include "ssh.h"
-#include "ssh1.h"
#include "ssh2.h"
-#include "rsa.h"
#include "sshpty.h"
#include "packet.h"
#include "log.h"
-#include "buffer.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 "key.h"
+#include "sshkey.h"
#include "kex.h"
#include "myproposal.h"
#include "authfile.h"
@@ -114,20 +113,16 @@
#include "dispatch.h"
#include "channels.h"
#include "session.h"
-#include "monitor_mm.h"
#include "monitor.h"
#ifdef GSSAPI
#include "ssh-gss.h"
#endif
#include "monitor_wrap.h"
-#include "roaming.h"
#include "ssh-sandbox.h"
+#include "auth-options.h"
#include "version.h"
#include "ssherr.h"
-
-#ifndef O_NOCTTY
-#define O_NOCTTY 0
-#endif
+#include "sk-api.h"
/* Re-exec fds */
#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1)
@@ -151,46 +146,44 @@ char *config_file_name = _PATH_SERVER_CONFIG_FILE;
*/
int debug_flag = 0;
-/* Flag indicating that the daemon should only test the configuration and keys. */
-int test_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. */
-int inetd_flag = 0;
+static int inetd_flag = 0;
/* Flag indicating that sshd should not detach and become a daemon. */
-int no_daemon_flag = 0;
+static int no_daemon_flag = 0;
/* debug goes to stderr unless inetd_flag is set */
-int log_stderr = 0;
+static int log_stderr = 0;
/* Saved arguments to main(). */
-char **saved_argv;
-int saved_argc;
+static char **saved_argv;
+static int saved_argc;
/* re-exec */
-int rexeced_flag = 0;
-int rexec_flag = 1;
-int rexec_argc = 0;
-char **rexec_argv;
+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.
*/
#define MAX_LISTEN_SOCKS 16
-int listen_socks[MAX_LISTEN_SOCKS];
-int num_listen_socks = 0;
-
-/*
- * the client's version string, passed by sshd2 in compat mode. if != NULL,
- * sshd will skip the version-number exchange
- */
-char *client_version_string = NULL;
-char *server_version_string = NULL;
+static int listen_socks[MAX_LISTEN_SOCKS];
+static int num_listen_socks = 0;
/* Daemon's agent connection */
int auth_sock = -1;
-int have_agent = 0;
+static int have_agent = 0;
/*
* Any really sensitive data in the application is contained in this
@@ -201,22 +194,12 @@ int have_agent = 0;
* not very useful. Currently, memory locking is not implemented.
*/
struct {
- Key *server_key; /* ephemeral server key */
- Key *ssh1_host_key; /* ssh1 host key */
- Key **host_keys; /* all private host keys */
- Key **host_pubkeys; /* all public host keys */
- Key **host_certificates; /* all public host certificates */
- int have_ssh1_key;
- int have_ssh2_key;
- u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH];
+ struct sshkey **host_keys; /* all private host keys */
+ struct sshkey **host_pubkeys; /* all public host keys */
+ struct sshkey **host_certificates; /* all public host certificates */
+ int have_ssh2_key;
} sensitive_data;
-/*
- * Flag indicating whether the RSA server key needs to be regenerated.
- * Is set in the SIGALRM handler and cleared when the key is regenerated.
- */
-static volatile sig_atomic_t key_do_regen = 0;
-
/* This is set to true when a signal is received. */
static volatile sig_atomic_t received_sighup = 0;
static volatile sig_atomic_t received_sigterm = 0;
@@ -231,23 +214,48 @@ u_int session_id2_len = 0;
/* record remote hostname or ip */
u_int utmp_len = HOST_NAME_MAX+1;
-/* options.max_startup sized array of fd ints */
-int *startup_pipes = NULL;
-int startup_pipe; /* in child */
+/*
+ * startup_pipes/flags are used for tracking children of the listening sshd
+ * process early in their lifespans. This tracking is needed for three things:
+ *
+ * 1) Implementing the MaxStartups limit of concurrent unauthenticated
+ * connections.
+ * 2) Avoiding a race condition for SIGHUP processing, where child processes
+ * may have listen_socks open that could collide with main listener process
+ * after it restarts.
+ * 3) Ensuring that rexec'd sshd processes have received their initial state
+ * from the parent listen process before handling SIGHUP.
+ *
+ * Child processes signal that they have completed closure of the listen_socks
+ * and (if applicable) received their rexec state by sending a char over their
+ * sock. Child processes signal that authentication has completed by closing
+ * the sock (or by exiting).
+ */
+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;
+static int privsep_chroot = 1;
-/* global authentication context */
+/* 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 */
-Buffer cfg;
+struct sshbuf *cfg;
+
+/* Included files from the configuration file */
+struct include_list includes = TAILQ_HEAD_INITIALIZER(includes);
/* message to be displayed after login */
-Buffer loginmsg;
+struct sshbuf *loginmsg;
/* Unprivileged user */
struct passwd *privsep_pw = NULL;
@@ -255,11 +263,9 @@ struct passwd *privsep_pw = NULL;
/* 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 *);
-#ifdef WITH_SSH1
-static void do_ssh1_kex(void);
-#endif
-static void do_ssh2_kex(void);
+static char *listener_proctitle;
/*
* Close all listening sockets
@@ -295,11 +301,7 @@ close_startup_pipes(void)
static void
sighup_handler(int sig)
{
- int save_errno = errno;
-
received_sighup = 1;
- signal(SIGHUP, sighup_handler);
- errno = save_errno;
}
/*
@@ -310,11 +312,12 @@ static void
sighup_restart(void)
{
logit("Received SIGHUP; restarting.");
+ if (options.pid_file != NULL)
+ unlink(options.pid_file);
platform_pre_restart();
close_listen_socks();
close_startup_pipes();
- alarm(0); /* alarm timer persists across exec */
- signal(SIGHUP, SIG_IGN); /* will be restored after exec */
+ ssh_signal(SIGHUP, SIG_IGN); /* will be restored after exec */
execv(saved_argv[0], saved_argv);
logit("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0],
strerror(errno));
@@ -343,11 +346,11 @@ main_sigchld_handler(int sig)
pid_t pid;
int status;
+ debug("main_sigchld_handler: %s", strsignal(sig));
+
while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
- (pid < 0 && errno == EINTR))
+ (pid == -1 && errno == EINTR))
;
-
- signal(SIGCHLD, main_sigchld_handler);
errno = save_errno;
}
@@ -366,248 +369,81 @@ grace_alarm_handler(int sig)
* keys command helpers.
*/
if (getpgid(0) == getpid()) {
- signal(SIGTERM, SIG_IGN);
+ ssh_signal(SIGTERM, SIG_IGN);
kill(0, SIGTERM);
}
+ /* XXX pre-format ipaddr/port so we don't need to access active_state */
/* Log error and exit. */
- sigdie("Timeout before authentication for %s", get_remote_ipaddr());
-}
-
-/*
- * Signal handler for the key regeneration alarm. Note that this
- * alarm only occurs in the daemon waiting for connections, and it does not
- * do anything with the private key or random state before forking.
- * Thus there should be no concurrency control/asynchronous execution
- * problems.
- */
-static void
-generate_ephemeral_server_key(void)
-{
- verbose("Generating %s%d bit RSA key.",
- sensitive_data.server_key ? "new " : "", options.server_key_bits);
- if (sensitive_data.server_key != NULL)
- key_free(sensitive_data.server_key);
- sensitive_data.server_key = key_generate(KEY_RSA1,
- options.server_key_bits);
- verbose("RSA key generation complete.");
-
- arc4random_buf(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH);
-}
-
-/*ARGSUSED*/
-static void
-key_regeneration_alarm(int sig)
-{
- int save_errno = errno;
-
- signal(SIGALRM, SIG_DFL);
- errno = save_errno;
- key_do_regen = 1;
-}
-
-static void
-sshd_exchange_identification(int sock_in, int sock_out)
-{
- u_int i;
- int mismatch;
- int remote_major, remote_minor;
- int major, minor;
- char *s, *newline = "\n";
- char buf[256]; /* Must not be larger than remote_version. */
- char remote_version[256]; /* Must be at least as big as buf. */
-
- if ((options.protocol & SSH_PROTO_1) &&
- (options.protocol & SSH_PROTO_2)) {
- major = PROTOCOL_MAJOR_1;
- minor = 99;
- } else if (options.protocol & SSH_PROTO_2) {
- major = PROTOCOL_MAJOR_2;
- minor = PROTOCOL_MINOR_2;
- newline = "\r\n";
- } else {
- major = PROTOCOL_MAJOR_1;
- minor = PROTOCOL_MINOR_1;
- }
-
- xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s%s",
- major, minor, SSH_VERSION,
- *options.version_addendum == '\0' ? "" : " ",
- options.version_addendum, newline);
-
- /* Send our protocol version identification. */
- if (roaming_atomicio(vwrite, sock_out, server_version_string,
- strlen(server_version_string))
- != strlen(server_version_string)) {
- logit("Could not write ident string to %s", get_remote_ipaddr());
- cleanup_exit(255);
- }
-
- /* Read other sides version identification. */
- memset(buf, 0, sizeof(buf));
- for (i = 0; i < sizeof(buf) - 1; i++) {
- if (roaming_atomicio(read, sock_in, &buf[i], 1) != 1) {
- logit("Did not receive identification string from %s",
- get_remote_ipaddr());
- cleanup_exit(255);
- }
- if (buf[i] == '\r') {
- buf[i] = 0;
- /* Kludge for F-Secure Macintosh < 1.0.2 */
- if (i == 12 &&
- strncmp(buf, "SSH-1.5-W1.0", 12) == 0)
- break;
- continue;
- }
- if (buf[i] == '\n') {
- buf[i] = 0;
- break;
- }
- }
- buf[sizeof(buf) - 1] = 0;
- client_version_string = xstrdup(buf);
-
- /*
- * Check that the versions match. In future this might accept
- * several versions and set appropriate flags to handle them.
- */
- if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n",
- &remote_major, &remote_minor, remote_version) != 3) {
- s = "Protocol mismatch.\n";
- (void) atomicio(vwrite, sock_out, s, strlen(s));
- logit("Bad protocol version identification '%.100s' "
- "from %s port %d", client_version_string,
- get_remote_ipaddr(), get_remote_port());
- close(sock_in);
- close(sock_out);
- cleanup_exit(255);
- }
- debug("Client protocol version %d.%d; client software version %.100s",
- remote_major, remote_minor, remote_version);
-
- active_state->compat = compat_datafellows(remote_version);
-
- if ((datafellows & SSH_BUG_PROBE) != 0) {
- logit("probed from %s with %s. Don't panic.",
- get_remote_ipaddr(), client_version_string);
- cleanup_exit(255);
- }
- if ((datafellows & SSH_BUG_SCANNER) != 0) {
- logit("scanned from %s with %s. Don't panic.",
- get_remote_ipaddr(), client_version_string);
- cleanup_exit(255);
- }
- if ((datafellows & SSH_BUG_RSASIGMD5) != 0) {
- logit("Client version \"%.100s\" uses unsafe RSA signature "
- "scheme; disabling use of RSA keys", remote_version);
- }
- if ((datafellows & SSH_BUG_DERIVEKEY) != 0) {
- fatal("Client version \"%.100s\" uses unsafe key agreement; "
- "refusing connection", remote_version);
- }
-
- mismatch = 0;
- switch (remote_major) {
- case 1:
- if (remote_minor == 99) {
- if (options.protocol & SSH_PROTO_2)
- enable_compat20();
- else
- mismatch = 1;
- break;
- }
- if (!(options.protocol & SSH_PROTO_1)) {
- mismatch = 1;
- break;
- }
- if (remote_minor < 3) {
- packet_disconnect("Your ssh version is too old and "
- "is no longer supported. Please install a newer version.");
- } else if (remote_minor == 3) {
- /* note that this disables agent-forwarding */
- enable_compat13();
- }
- break;
- case 2:
- if (options.protocol & SSH_PROTO_2) {
- enable_compat20();
- break;
- }
- /* FALLTHROUGH */
- default:
- mismatch = 1;
- break;
- }
- chop(server_version_string);
- debug("Local version string %.200s", server_version_string);
-
- if (mismatch) {
- s = "Protocol major versions differ.\n";
- (void) atomicio(vwrite, sock_out, s, strlen(s));
- close(sock_in);
- close(sock_out);
- logit("Protocol major versions differ for %s: %.200s vs. %.200s",
- get_remote_ipaddr(),
- server_version_string, client_version_string);
- cleanup_exit(255);
- }
+ 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)
{
- int i;
+ u_int i;
- if (sensitive_data.server_key) {
- key_free(sensitive_data.server_key);
- sensitive_data.server_key = NULL;
- }
for (i = 0; i < options.num_host_key_files; i++) {
if (sensitive_data.host_keys[i]) {
- key_free(sensitive_data.host_keys[i]);
+ sshkey_free(sensitive_data.host_keys[i]);
sensitive_data.host_keys[i] = NULL;
}
if (sensitive_data.host_certificates[i]) {
- key_free(sensitive_data.host_certificates[i]);
+ sshkey_free(sensitive_data.host_certificates[i]);
sensitive_data.host_certificates[i] = NULL;
}
}
- sensitive_data.ssh1_host_key = NULL;
- explicit_bzero(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH);
}
/* Demote private to public keys for network child */
void
demote_sensitive_data(void)
{
- Key *tmp;
- int i;
-
- if (sensitive_data.server_key) {
- tmp = key_demote(sensitive_data.server_key);
- key_free(sensitive_data.server_key);
- sensitive_data.server_key = tmp;
- }
+ struct sshkey *tmp;
+ u_int i;
+ int r;
for (i = 0; i < options.num_host_key_files; i++) {
if (sensitive_data.host_keys[i]) {
- tmp = key_demote(sensitive_data.host_keys[i]);
- key_free(sensitive_data.host_keys[i]);
+ if ((r = sshkey_from_private(
+ sensitive_data.host_keys[i], &tmp)) != 0)
+ fatal("could not demote host %s key: %s",
+ sshkey_type(sensitive_data.host_keys[i]),
+ ssh_err(r));
+ sshkey_free(sensitive_data.host_keys[i]);
sensitive_data.host_keys[i] = tmp;
- if (tmp->type == KEY_RSA1)
- sensitive_data.ssh1_host_key = tmp;
}
/* Certs do not need demotion */
}
+}
+
+static void
+reseed_prngs(void)
+{
+ u_int32_t rnd[256];
- /* We do not clear ssh1_host key and cookie. XXX - Okay Niels? */
+#ifdef WITH_OPENSSL
+ RAND_poll();
+#endif
+ arc4random_stir(); /* noop on recent arc4random() implementations */
+ arc4random_buf(rnd, sizeof(rnd)); /* let arc4random notice PID change */
+
+#ifdef WITH_OPENSSL
+ RAND_seed(rnd, sizeof(rnd));
+ /* give libcrypto a chance to notice the PID change */
+ if ((RAND_bytes((u_char *)rnd, 1)) != 1)
+ fatal("%s: RAND_bytes failed", __func__);
+#endif
+
+ explicit_bzero(rnd, sizeof(rnd));
}
static void
privsep_preauth_child(void)
{
- u_int32_t rnd[256];
gid_t gidset[1];
/* Enable challenge-response authentication for privilege separation */
@@ -615,45 +451,35 @@ privsep_preauth_child(void)
#ifdef GSSAPI
/* Cache supported mechanism OIDs for later use */
- if (options.gss_authentication)
- ssh_gssapi_prepare_supported_oids();
+ ssh_gssapi_prepare_supported_oids();
#endif
- arc4random_stir();
- arc4random_buf(rnd, sizeof(rnd));
-#ifdef WITH_OPENSSL
- RAND_seed(rnd, sizeof(rnd));
- if ((RAND_bytes((u_char *)rnd, 1)) != 1)
- fatal("%s: RAND_bytes failed", __func__);
-#endif
- explicit_bzero(rnd, sizeof(rnd));
+ reseed_prngs();
/* Demote the private keys to public keys. */
demote_sensitive_data();
- /* 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);
-#if 0
- /* XXX not ready, too heavy after chroot */
- do_setusercontext(privsep_pw);
-#else
- gidset[0] = privsep_pw->pw_gid;
- if (setgroups(1, gidset) < 0)
- fatal("setgroups: %.100s", strerror(errno));
- permanently_set_uid(privsep_pw);
-#endif
+ /* 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);
+ }
}
static int
-privsep_preauth(Authctxt *authctxt)
+privsep_preauth(struct ssh *ssh)
{
int status, r;
pid_t pid;
@@ -662,7 +488,7 @@ privsep_preauth(Authctxt *authctxt)
/* 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 = &active_state->kex;
+ pmonitor->m_pkex = &ssh->kex;
if (use_privsep == PRIVSEP_ON)
box = ssh_sandbox_init(pmonitor);
@@ -683,13 +509,10 @@ privsep_preauth(Authctxt *authctxt)
}
if (box != NULL)
ssh_sandbox_parent_preauth(box, pid);
- monitor_child_preauth(authctxt, pmonitor);
-
- /* Sync memory */
- monitor_sync(pmonitor);
+ monitor_child_preauth(ssh, pmonitor);
/* Wait for the child's exit status */
- while (waitpid(pid, &status, 0) < 0) {
+ while (waitpid(pid, &status, 0) == -1) {
if (errno == EINTR)
continue;
pmonitor->m_pid = -1;
@@ -715,9 +538,7 @@ privsep_preauth(Authctxt *authctxt)
/* Arrange for logging to be sent to the monitor */
set_log_handler(mm_log_handler, pmonitor);
- /* Demote the child */
- if (getuid() == 0 || geteuid() == 0)
- privsep_preauth_child();
+ privsep_preauth_child();
setproctitle("%s", "[net]");
if (box != NULL)
ssh_sandbox_child(box);
@@ -727,14 +548,12 @@ privsep_preauth(Authctxt *authctxt)
}
static void
-privsep_postauth(Authctxt *authctxt)
+privsep_postauth(struct ssh *ssh, Authctxt *authctxt)
{
- u_int32_t rnd[256];
-
#ifdef DISABLE_FD_PASSING
if (1) {
#else
- if (authctxt->pw->pw_uid == 0 || options.use_login) {
+ if (authctxt->pw->pw_uid == 0) {
#endif
/* File descriptor passing is broken or root login */
use_privsep = 0;
@@ -749,8 +568,9 @@ privsep_postauth(Authctxt *authctxt)
fatal("fork of unprivileged child failed");
else if (pmonitor->m_pid != 0) {
verbose("User child is on pid %ld", (long)pmonitor->m_pid);
- buffer_clear(&loginmsg);
- monitor_child_postauth(pmonitor);
+ sshbuf_reset(loginmsg);
+ monitor_clear_keystate(ssh, pmonitor);
+ monitor_child_postauth(ssh, pmonitor);
/* NEVERREACHED */
exit(0);
@@ -764,39 +584,46 @@ privsep_postauth(Authctxt *authctxt)
/* Demote the private keys to public keys. */
demote_sensitive_data();
- arc4random_stir();
- arc4random_buf(rnd, sizeof(rnd));
-#ifdef WITH_OPENSSL
- RAND_seed(rnd, sizeof(rnd));
- if ((RAND_bytes((u_char *)rnd, 1)) != 1)
- fatal("%s: RAND_bytes failed", __func__);
-#endif
- explicit_bzero(rnd, sizeof(rnd));
+ reseed_prngs();
/* Drop privileges */
do_setusercontext(authctxt->pw);
skip:
/* It is safe now to apply the key state */
- monitor_apply_keystate(pmonitor);
+ monitor_apply_keystate(ssh, pmonitor);
/*
* Tell the packet layer that authentication was successful, since
* this information is not part of the key state.
*/
- packet_set_authenticated();
+ 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("%s: %s key not permitted by HostkeyAlgorithms",
+ __func__, s);
+ return;
+ }
+ if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) > 0 ? "," : "", s)) != 0)
+ fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
}
static char *
list_hostkey_types(void)
{
- Buffer b;
- const char *p;
+ struct sshbuf *b;
+ struct sshkey *key;
char *ret;
- int i;
- Key *key;
+ u_int i;
- buffer_init(&b);
+ if ((b = sshbuf_new()) == NULL)
+ fatal("%s: sshbuf_new failed", __func__);
for (i = 0; i < options.num_host_key_files; i++) {
key = sensitive_data.host_keys[i];
if (key == NULL)
@@ -805,13 +632,17 @@ list_hostkey_types(void)
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:
- if (buffer_len(&b) > 0)
- buffer_append(&b, ",", 1);
- p = key_ssh_name(key);
- buffer_append(&b, p, strlen(p));
+ 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 */
@@ -819,40 +650,45 @@ list_hostkey_types(void)
if (key == NULL)
continue;
switch (key->type) {
- case KEY_RSA_CERT_V00:
- case KEY_DSA_CERT_V00:
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:
- if (buffer_len(&b) > 0)
- buffer_append(&b, ",", 1);
- p = key_ssh_name(key);
- buffer_append(&b, p, strlen(p));
+ case KEY_ECDSA_SK_CERT:
+ case KEY_ED25519_SK_CERT:
+ case KEY_XMSS_CERT:
+ append_hostkey_type(b, sshkey_ssh_name(key));
break;
}
}
- buffer_append(&b, "\0", 1);
- ret = xstrdup(buffer_ptr(&b));
- buffer_free(&b);
- debug("list_hostkey_types: %s", ret);
+ if ((ret = sshbuf_dup_string(b)) == NULL)
+ fatal("%s: sshbuf_dup_string failed", __func__);
+ sshbuf_free(b);
+ debug("%s: %s", __func__, ret);
return ret;
}
-static Key *
+static struct sshkey *
get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
{
- int i;
- Key *key;
+ u_int i;
+ struct sshkey *key;
for (i = 0; i < options.num_host_key_files; i++) {
switch (type) {
- case KEY_RSA_CERT_V00:
- case KEY_DSA_CERT_V00:
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:
@@ -861,49 +697,59 @@ get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
key = sensitive_data.host_pubkeys[i];
break;
}
- if (key != NULL && key->type == type &&
- (key->type != KEY_ECDSA || key->ecdsa_nid == nid))
+ 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;
}
-Key *
+struct sshkey *
get_hostkey_public_by_type(int type, int nid, struct ssh *ssh)
{
return get_hostkey_by_type(type, nid, 0, ssh);
}
-Key *
+struct sshkey *
get_hostkey_private_by_type(int type, int nid, struct ssh *ssh)
{
return get_hostkey_by_type(type, nid, 1, ssh);
}
-Key *
+struct sshkey *
get_hostkey_by_index(int ind)
{
- if (ind < 0 || ind >= options.num_host_key_files)
+ if (ind < 0 || (u_int)ind >= options.num_host_key_files)
return (NULL);
return (sensitive_data.host_keys[ind]);
}
-Key *
+struct sshkey *
get_hostkey_public_by_index(int ind, struct ssh *ssh)
{
- if (ind < 0 || ind >= options.num_host_key_files)
+ if (ind < 0 || (u_int)ind >= options.num_host_key_files)
return (NULL);
return (sensitive_data.host_pubkeys[ind]);
}
int
-get_hostkey_index(Key *key, int compare, struct ssh *ssh)
+get_hostkey_index(struct sshkey *key, int compare, struct ssh *ssh)
{
- int i;
+ u_int i;
for (i = 0; i < options.num_host_key_files; i++) {
- if (key_is_cert(key)) {
+ if (sshkey_is_cert(key)) {
if (key == sensitive_data.host_certificates[i] ||
(compare && sensitive_data.host_certificates[i] &&
sshkey_equal(key,
@@ -929,15 +775,20 @@ notify_hostkeys(struct ssh *ssh)
{
struct sshbuf *buf;
struct sshkey *key;
- int i, nkeys, r;
+ 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("%s: sshbuf_new", __func__);
for (i = nkeys = 0; i < options.num_host_key_files; i++) {
key = get_hostkey_public_by_index(i, ssh);
if (key == NULL || key->type == KEY_UNSPEC ||
- key->type == KEY_RSA1 || sshkey_is_cert(key))
+ sshkey_is_cert(key))
continue;
fp = sshkey_fingerprint(key, options.fingerprint_hash,
SSH_FP_DEFAULT);
@@ -945,21 +796,29 @@ notify_hostkeys(struct ssh *ssh)
sshkey_ssh_name(key), fp);
free(fp);
if (nkeys == 0) {
- packet_start(SSH2_MSG_GLOBAL_REQUEST);
- packet_put_cstring("hostkeys-00@openssh.com");
- packet_put_char(0); /* want-reply */
+ /*
+ * 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("%s: couldn't put hostkey %d: %s",
__func__, i, ssh_err(r));
- packet_put_string(sshbuf_ptr(buf), sshbuf_len(buf));
+ if ((r = sshpkt_put_stringb(ssh, buf)) != 0)
+ sshpkt_fatal(ssh, r, "%s: append key", __func__);
nkeys++;
}
- debug3("%s: sent %d hostkeys", __func__, nkeys);
+ debug3("%s: sent %u hostkeys", __func__, nkeys);
if (nkeys == 0)
fatal("%s: no hostkeys", __func__);
- packet_send();
+ if ((r = sshpkt_send(ssh)) != 0)
+ sshpkt_fatal(ssh, r, "%s: send", __func__);
sshbuf_free(buf);
}
@@ -997,115 +856,108 @@ usage(void)
fprintf(stderr, "%s, %s\n",
SSH_RELEASE,
#ifdef WITH_OPENSSL
- SSLeay_version(SSLEAY_VERSION)
+ OpenSSL_version(OPENSSL_VERSION)
#else
"without OpenSSL"
#endif
);
fprintf(stderr,
-"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_cert_file]\n"
+"usage: sshd [-46DdeiqTt] [-C connection_spec] [-c host_cert_file]\n"
" [-E log_file] [-f config_file] [-g login_grace_time]\n"
-" [-h host_key_file] [-k key_gen_time] [-o option] [-p port]\n"
-" [-u len]\n"
+" [-h host_key_file] [-o option] [-p port] [-u len]\n"
);
exit(1);
}
static void
-send_rexec_state(int fd, Buffer *conf)
+send_rexec_state(int fd, struct sshbuf *conf)
{
- Buffer m;
+ struct sshbuf *m = NULL, *inc = NULL;
+ struct include_item *item = NULL;
+ int r;
+
+ debug3("%s: entering fd = %d config len %zu", __func__, fd,
+ sshbuf_len(conf));
- debug3("%s: entering fd = %d config len %d", __func__, fd,
- buffer_len(conf));
+ if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL)
+ fatal("%s: sshbuf_new failed", __func__);
+
+ /* pack includes into a string */
+ TAILQ_FOREACH(item, &includes, entry) {
+ if ((r = sshbuf_put_cstring(inc, item->selector)) != 0 ||
+ (r = sshbuf_put_cstring(inc, item->filename)) != 0 ||
+ (r = sshbuf_put_stringb(inc, item->contents)) != 0)
+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ }
/*
* Protocol from reexec master to child:
* string configuration
- * u_int ephemeral_key_follows
- * bignum e (only if ephemeral_key_follows == 1)
- * bignum n "
- * bignum d "
- * bignum iqmp "
- * bignum p "
- * bignum q "
- * string rngseed (only if OpenSSL is not self-seeded)
+ * string included_files[] {
+ * string selector
+ * string filename
+ * string contents
+ * }
+ * string rng_seed (if required)
*/
- buffer_init(&m);
- buffer_put_cstring(&m, buffer_ptr(conf));
-
-#ifdef WITH_SSH1
- if (sensitive_data.server_key != NULL &&
- sensitive_data.server_key->type == KEY_RSA1) {
- buffer_put_int(&m, 1);
- buffer_put_bignum(&m, sensitive_data.server_key->rsa->e);
- buffer_put_bignum(&m, sensitive_data.server_key->rsa->n);
- buffer_put_bignum(&m, sensitive_data.server_key->rsa->d);
- buffer_put_bignum(&m, sensitive_data.server_key->rsa->iqmp);
- buffer_put_bignum(&m, sensitive_data.server_key->rsa->p);
- buffer_put_bignum(&m, sensitive_data.server_key->rsa->q);
- } else
-#endif
- buffer_put_int(&m, 0);
-
+ if ((r = sshbuf_put_stringb(m, conf)) != 0 ||
+ (r = sshbuf_put_stringb(m, inc)) != 0)
+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
#if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY)
- rexec_send_rng_seed(&m);
+ rexec_send_rng_seed(m);
#endif
-
- if (ssh_msg_send(fd, 0, &m) == -1)
+ if (ssh_msg_send(fd, 0, m) == -1)
fatal("%s: ssh_msg_send failed", __func__);
- buffer_free(&m);
+ sshbuf_free(m);
+ sshbuf_free(inc);
debug3("%s: done", __func__);
}
static void
-recv_rexec_state(int fd, Buffer *conf)
+recv_rexec_state(int fd, struct sshbuf *conf)
{
- Buffer m;
- char *cp;
- u_int len;
+ struct sshbuf *m, *inc;
+ u_char *cp, ver;
+ size_t len;
+ int r;
+ struct include_item *item;
debug3("%s: entering fd = %d", __func__, fd);
- buffer_init(&m);
-
- if (ssh_msg_recv(fd, &m) == -1)
+ if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL)
+ fatal("%s: sshbuf_new failed", __func__);
+ if (ssh_msg_recv(fd, m) == -1)
fatal("%s: ssh_msg_recv failed", __func__);
- if (buffer_get_char(&m) != 0)
+ if ((r = sshbuf_get_u8(m, &ver)) != 0)
+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ if (ver != 0)
fatal("%s: rexec version mismatch", __func__);
-
- cp = buffer_get_string(&m, &len);
- if (conf != NULL)
- buffer_append(conf, cp, len + 1);
- free(cp);
-
- if (buffer_get_int(&m)) {
-#ifdef WITH_SSH1
- if (sensitive_data.server_key != NULL)
- key_free(sensitive_data.server_key);
- sensitive_data.server_key = key_new_private(KEY_RSA1);
- buffer_get_bignum(&m, sensitive_data.server_key->rsa->e);
- buffer_get_bignum(&m, sensitive_data.server_key->rsa->n);
- buffer_get_bignum(&m, sensitive_data.server_key->rsa->d);
- buffer_get_bignum(&m, sensitive_data.server_key->rsa->iqmp);
- buffer_get_bignum(&m, sensitive_data.server_key->rsa->p);
- buffer_get_bignum(&m, sensitive_data.server_key->rsa->q);
- if (rsa_generate_additional_parameters(
- sensitive_data.server_key->rsa) != 0)
- fatal("%s: rsa_generate_additional_parameters "
- "error", __func__);
-#else
- fatal("ssh1 not supported");
-#endif
- }
+ if ((r = sshbuf_get_string(m, &cp, &len)) != 0 ||
+ (r = sshbuf_get_stringb(m, inc)) != 0)
+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
#if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY)
- rexec_recv_rng_seed(&m);
+ rexec_recv_rng_seed(m);
#endif
- buffer_free(&m);
+ if (conf != NULL && (r = sshbuf_put(conf, cp, len)))
+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
+ while (sshbuf_len(inc) != 0) {
+ item = xcalloc(1, sizeof(*item));
+ if ((item->contents = sshbuf_new()) == NULL)
+ fatal("%s: sshbuf_new failed", __func__);
+ 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("%s: buffer error: %s", __func__, ssh_err(r));
+ TAILQ_INSERT_TAIL(&includes, item, entry);
+ }
+
+ free(cp);
+ sshbuf_free(m);
debug3("%s: done", __func__);
}
@@ -1116,14 +968,9 @@ server_accept_inetd(int *sock_in, int *sock_out)
{
int fd;
- startup_pipe = -1;
if (rexeced_flag) {
close(REEXEC_CONFIG_PASS_FD);
*sock_in = *sock_out = dup(STDIN_FILENO);
- if (!debug_flag) {
- startup_pipe = dup(REEXEC_STARTUP_PIPE_FD);
- close(REEXEC_STARTUP_PIPE_FD);
- }
} else {
*sock_in = dup(STDIN_FILENO);
*sock_out = dup(STDOUT_FILENO);
@@ -1148,13 +995,13 @@ server_accept_inetd(int *sock_in, int *sock_out)
* Listen for TCP connections
*/
static void
-server_listen(void)
+listen_on_addrs(struct listenaddr *la)
{
- int ret, listen_sock, on = 1;
+ int ret, listen_sock;
struct addrinfo *ai;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
- for (ai = options.listen_addrs; ai; ai = ai->ai_next) {
+ for (ai = la->addrs; ai; ai = ai->ai_next) {
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
continue;
if (num_listen_socks >= MAX_LISTEN_SOCKS)
@@ -1170,7 +1017,7 @@ server_listen(void)
/* Create socket for listening. */
listen_sock = socket(ai->ai_family, ai->ai_socktype,
ai->ai_protocol);
- if (listen_sock < 0) {
+ if (listen_sock == -1) {
/* kernel may not support ipv6 */
verbose("socket: %.100s", strerror(errno));
continue;
@@ -1179,13 +1026,18 @@ server_listen(void)
close(listen_sock);
continue;
}
- /*
- * Set socket options.
- * Allow local port reuse in TIME_WAIT.
- */
- if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
- &on, sizeof(on)) == -1)
- error("setsockopt SO_REUSEADDR: %s", strerror(errno));
+ if (fcntl(listen_sock, F_SETFD, FD_CLOEXEC) == -1) {
+ verbose("socket: CLOEXEC: %s", strerror(errno));
+ close(listen_sock);
+ continue;
+ }
+ /* Socket options */
+ set_reuseaddr(listen_sock);
+ if (la->rdomain != NULL &&
+ set_rdomain(listen_sock, la->rdomain) == -1) {
+ close(listen_sock);
+ continue;
+ }
/* Only communicate in IPv6 over AF_INET6 sockets. */
if (ai->ai_family == AF_INET6)
@@ -1194,7 +1046,7 @@ server_listen(void)
debug("Bind to port %s on %s.", strport, ntop);
/* Bind the socket to the desired port. */
- if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) {
+ if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) == -1) {
error("Bind to port %s on %s failed: %.200s.",
strport, ntop, strerror(errno));
close(listen_sock);
@@ -1204,12 +1056,31 @@ server_listen(void)
num_listen_socks++;
/* Start listening on the port. */
- if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0)
+ if (listen(listen_sock, SSH_LISTEN_BACKLOG) == -1)
fatal("listen on [%s]:%s: %.100s",
ntop, strport, strerror(errno));
- logit("Server listening on %s port %s.", ntop, strport);
+ logit("Server listening on %s port %s%s%s.",
+ ntop, strport,
+ la->rdomain == NULL ? "" : " rdomain ",
+ la->rdomain == NULL ? "" : la->rdomain);
+ }
+}
+
+static void
+server_listen(void)
+{
+ u_int i;
+
+ for (i = 0; i < options.num_listen_addrs; i++) {
+ listen_on_addrs(&options.listen_addrs[i]);
+ freeaddrinfo(options.listen_addrs[i].addrs);
+ free(options.listen_addrs[i].rdomain);
+ memset(&options.listen_addrs[i], 0,
+ sizeof(options.listen_addrs[i]));
}
- freeaddrinfo(options.listen_addrs);
+ free(options.listen_addrs);
+ options.listen_addrs = NULL;
+ options.num_listen_addrs = 0;
if (!num_listen_socks)
fatal("Cannot bind any address.");
@@ -1224,8 +1095,9 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
{
fd_set *fdset;
int i, j, ret, maxfd;
- int key_used = 0, startups = 0;
+ int ostartups = -1, startups = 0, listening = 0, lameduck = 0;
int startup_p[2] = { -1 , -1 };
+ char c = 0;
struct sockaddr_storage from;
socklen_t fromlen;
pid_t pid;
@@ -1237,8 +1109,9 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
for (i = 0; i < num_listen_socks; i++)
if (listen_socks[i] > maxfd)
maxfd = listen_socks[i];
- /* pipes connected to unauthenticated childs */
+ /* pipes connected to unauthenticated child sshd processes */
startup_pipes = xcalloc(options.max_startups, sizeof(int));
+ startup_flags = xcalloc(options.max_startups, sizeof(int));
for (i = 0; i < options.max_startups; i++)
startup_pipes[i] = -1;
@@ -1247,11 +1120,23 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
* the daemon is killed with a signal.
*/
for (;;) {
- if (received_sighup)
- sighup_restart();
- if (fdset != NULL)
- free(fdset);
- fdset = (fd_set *)xcalloc(howmany(maxfd + 1, NFDBITS),
+ if (ostartups != startups) {
+ setproctitle("%s [listener] %d of %d-%d startups",
+ listener_proctitle, startups,
+ options.max_startups_begin, options.max_startups);
+ ostartups = startups;
+ }
+ if (received_sighup) {
+ if (!lameduck) {
+ debug("Received SIGHUP; waiting for children");
+ close_listen_socks();
+ lameduck = 1;
+ }
+ if (listening <= 0)
+ sighup_restart();
+ }
+ free(fdset);
+ fdset = xcalloc(howmany(maxfd + 1, NFDBITS),
sizeof(fd_mask));
for (i = 0; i < num_listen_socks; i++)
@@ -1262,7 +1147,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
/* Wait in select until there is a connection. */
ret = select(maxfd+1, fdset, NULL, NULL, NULL);
- if (ret < 0 && errno != EINTR)
+ if (ret == -1 && errno != EINTR)
error("select: %.100s", strerror(errno));
if (received_sigterm) {
logit("Received signal %d; terminating.",
@@ -1272,34 +1157,47 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
unlink(options.pid_file);
exit(received_sigterm == SIGTERM ? 0 : 255);
}
- if (key_used && key_do_regen) {
- generate_ephemeral_server_key();
- key_used = 0;
- key_do_regen = 0;
- }
- if (ret < 0)
+ if (ret == -1)
continue;
- for (i = 0; i < options.max_startups; i++)
- if (startup_pipes[i] != -1 &&
- FD_ISSET(startup_pipes[i], fdset)) {
- /*
- * the read end of the pipe is ready
- * if the child has closed the pipe
- * after successful authentication
- * or if the child has died
- */
+ for (i = 0; i < options.max_startups; i++) {
+ if (startup_pipes[i] == -1 ||
+ !FD_ISSET(startup_pipes[i], fdset))
+ continue;
+ switch (read(startup_pipes[i], &c, sizeof(c))) {
+ case -1:
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ if (errno != EPIPE) {
+ error("%s: startup pipe %d (fd=%d): "
+ "read %s", __func__, i,
+ startup_pipes[i], strerror(errno));
+ }
+ /* FALLTHROUGH */
+ case 0:
+ /* child exited or completed auth */
close(startup_pipes[i]);
startup_pipes[i] = -1;
startups--;
+ if (startup_flags[i])
+ listening--;
+ break;
+ case 1:
+ /* child has finished preliminaries */
+ if (startup_flags[i]) {
+ listening--;
+ startup_flags[i] = 0;
+ }
+ break;
}
+ }
for (i = 0; i < num_listen_socks; i++) {
if (!FD_ISSET(listen_socks[i], fdset))
continue;
fromlen = sizeof(from);
*newsock = accept(listen_socks[i],
(struct sockaddr *)&from, &fromlen);
- if (*newsock < 0) {
+ if (*newsock == -1) {
if (errno != EINTR && errno != EWOULDBLOCK &&
errno != ECONNABORTED && errno != EAGAIN)
error("accept: %.100s",
@@ -1313,7 +1211,18 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
continue;
}
if (drop_connection(startups) == 1) {
- debug("drop connection #%d", startups);
+ char *laddr = get_local_ipaddr(*newsock);
+ char *raddr = get_peer_ipaddr(*newsock);
+ char msg[] = "Exceeded MaxStartups\r\n";
+
+ verbose("drop connection #%d from [%s]:%d "
+ "on [%s]:%d past MaxStartups", startups,
+ raddr, get_peer_port(*newsock),
+ laddr, get_local_port(*newsock));
+ free(laddr);
+ free(raddr);
+ /* best-effort notification to client */
+ (void)write(*newsock, msg, strlen(msg));
close(*newsock);
continue;
}
@@ -1338,6 +1247,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
if (maxfd < startup_p[0])
maxfd = startup_p[0];
startups++;
+ startup_flags[j] = 1;
break;
}
@@ -1360,11 +1270,10 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
startup_pipe = -1;
pid = getpid();
if (rexec_flag) {
- send_rexec_state(config_s[0],
- &cfg);
+ send_rexec_state(config_s[0], cfg);
close(config_s[0]);
}
- break;
+ return;
}
/*
@@ -1373,13 +1282,14 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
* parent continues listening.
*/
platform_pre_fork();
+ listening++;
if ((pid = fork()) == 0) {
/*
* Child. Close the listening and
* max_startup sockets. Start using
* the accepted socket. Reinitialize
* logging (since our pid has changed).
- * We break out of the loop to handle
+ * We return from this function to handle
* the connection.
*/
platform_post_fork_child();
@@ -1394,12 +1304,23 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
log_stderr);
if (rexec_flag)
close(config_s[0]);
- break;
+ 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);
+ }
+ return;
}
/* Parent. Stay in the loop. */
platform_post_fork_parent(pid);
- if (pid < 0)
+ if (pid == -1)
error("fork: %.100s", strerror(errno));
else
debug("Forked child %ld.", (long)pid);
@@ -1407,23 +1328,10 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
close(startup_p[1]);
if (rexec_flag) {
- send_rexec_state(config_s[0], &cfg);
+ send_rexec_state(config_s[0], cfg);
close(config_s[0]);
close(config_s[1]);
}
-
- /*
- * Mark that the key has been used (it
- * was "given" to the child).
- */
- if ((options.protocol & SSH_PROTO_1) &&
- key_used == 0) {
- /* Schedule server key regeneration alarm. */
- signal(SIGALRM, key_regeneration_alarm);
- alarm(options.key_regeneration_time);
- key_used = 1;
- }
-
close(*newsock);
/*
@@ -1439,13 +1347,138 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
#endif
explicit_bzero(rnd, sizeof(rnd));
}
+ }
+}
- /* child process check (or debug mode) */
- if (num_listen_socks < 0)
- break;
+/*
+ * 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)
+{
+#ifdef IP_OPTIONS
+ 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;
+#endif /* IP_OPTIONS */
+}
+
+/* Set the routing domain for this process */
+static void
+set_process_rdomain(struct ssh *ssh, const char *name)
+{
+#if defined(HAVE_SYS_SET_PROCESS_RDOMAIN)
+ 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;
+ }
+ /* NB. We don't pass 'ssh' to sys_set_process_rdomain() */
+ return sys_set_process_rdomain(name);
+#elif defined(__OpenBSD__)
+ 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("%s: set routing domain %d (was %d)", __func__, rtable, ortable);
+#else /* defined(__OpenBSD__) */
+ fatal("Unable to set routing domain: not supported in this platform");
+#endif
+}
+
+static void
+accumulate_host_timing_secret(struct sshbuf *server_cfg,
+ struct sshkey *key)
+{
+ static struct ssh_digest_ctx *ctx;
+ u_char *hash;
+ size_t len;
+ struct sshbuf *buf;
+ int r;
+
+ if (ctx == NULL && (ctx = ssh_digest_start(SSH_DIGEST_SHA512)) == NULL)
+ fatal("%s: ssh_digest_start", __func__);
+ if (key == NULL) { /* finalize */
+ /* add server config in case we are using agent for host keys */
+ if (ssh_digest_update(ctx, sshbuf_ptr(server_cfg),
+ sshbuf_len(server_cfg)) != 0)
+ fatal("%s: ssh_digest_update", __func__);
+ len = ssh_digest_bytes(SSH_DIGEST_SHA512);
+ hash = xmalloc(len);
+ if (ssh_digest_final(ctx, hash, len) != 0)
+ fatal("%s: ssh_digest_final", __func__);
+ options.timing_secret = PEEK_U64(hash);
+ freezero(hash, len);
+ ssh_digest_free(ctx);
+ ctx = NULL;
+ return;
}
+ if ((buf = sshbuf_new()) == NULL)
+ fatal("%s could not allocate buffer", __func__);
+ if ((r = sshkey_private_serialize(key, buf)) != 0)
+ fatal("sshkey_private_serialize: %s", ssh_err(r));
+ if (ssh_digest_update(ctx, sshbuf_ptr(buf), sshbuf_len(buf)) != 0)
+ fatal("%s: ssh_digest_update", __func__);
+ sshbuf_reset(buf);
+ sshbuf_free(buf);
}
+static char *
+prepare_proctitle(int ac, char **av)
+{
+ char *ret = NULL;
+ int i;
+
+ for (i = 0; i < ac; i++)
+ xextendf(&ret, " ", "%s", av[i]);
+ return ret;
+}
/*
* Main program for the daemon.
@@ -1453,22 +1486,22 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
int
main(int ac, char **av)
{
+ struct ssh *ssh = NULL;
extern char *optarg;
extern int optind;
- int r, opt, i, j, on = 1;
+ int r, opt, on = 1, already_daemon, remote_port;
int sock_in = -1, sock_out = -1, newsock = -1;
- const char *remote_ip;
- int remote_port;
- char *fp, *line, *logfile = NULL;
+ const char *remote_ip, *rdomain;
+ char *fp, *line, *laddr, *logfile = NULL;
int config_s[2] = { -1 , -1 };
- u_int n;
+ u_int i, j;
u_int64_t ibytes, obytes;
mode_t new_umask;
- Key *key;
- Key *pubkey;
+ struct sshkey *key;
+ struct sshkey *pubkey;
int keytype;
Authctxt *authctxt;
- struct connection_info *connection_info = get_connection_info(0, 0);
+ struct connection_info *connection_info = NULL;
#ifdef HAVE_SECUREWARE
(void)set_auth_parameters(ac, av);
@@ -1479,7 +1512,7 @@ main(int ac, char **av)
saved_argc = ac;
rexec_argc = ac;
saved_argv = xcalloc(ac + 1, sizeof(*saved_argv));
- for (i = 0; i < ac; i++)
+ for (i = 0; (int)i < ac; i++)
saved_argv[i] = xstrdup(av[i]);
saved_argv[i] = NULL;
@@ -1495,11 +1528,14 @@ main(int ac, char **av)
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd();
+ seed_rng();
+
/* Initialize configuration options to their default values. */
initialize_server_options(&options);
/* Parse command-line arguments. */
- while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:C:dDeE:iqrtQRT46")) != -1) {
+ while ((opt = getopt(ac, av,
+ "C:E:b:c:f:g:h:k:o:p:u:46DQRTdeiqrt")) != -1) {
switch (opt) {
case '4':
options.address_family = AF_INET;
@@ -1511,12 +1547,8 @@ main(int ac, char **av)
config_file_name = optarg;
break;
case 'c':
- if (options.num_host_cert_files >= MAX_HOSTCERTS) {
- fprintf(stderr, "too many host certificates.\n");
- exit(1);
- }
- options.host_cert_files[options.num_host_cert_files++] =
- derelativise_path(optarg);
+ servconf_add_hostcert("[command-line]", 0,
+ &options, optarg);
break;
case 'd':
if (debug_flag == 0) {
@@ -1529,7 +1561,7 @@ main(int ac, char **av)
no_daemon_flag = 1;
break;
case 'E':
- logfile = xstrdup(optarg);
+ logfile = optarg;
/* FALLTHROUGH */
case 'e':
log_stderr = 1;
@@ -1551,8 +1583,7 @@ main(int ac, char **av)
options.log_level = SYSLOG_LEVEL_QUIET;
break;
case 'b':
- options.server_key_bits = (int)strtonum(optarg, 256,
- 32768, NULL);
+ /* protocol 1, ignored */
break;
case 'p':
options.ports_from_cmdline = 1;
@@ -1573,18 +1604,11 @@ main(int ac, char **av)
}
break;
case 'k':
- if ((options.key_regeneration_time = convtime(optarg)) == -1) {
- fprintf(stderr, "Invalid key regeneration interval.\n");
- exit(1);
- }
+ /* protocol 1, ignored */
break;
case 'h':
- if (options.num_host_key_files >= MAX_HOSTKEYS) {
- fprintf(stderr, "too many host keys.\n");
- exit(1);
- }
- options.host_key_files[options.num_host_key_files++] =
- derelativise_path(optarg);
+ servconf_add_hostkey("[command-line]", 0,
+ &options, optarg, 1);
break;
case 't':
test_flag = 1;
@@ -1593,6 +1617,7 @@ 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,
optarg) == -1)
exit(1);
@@ -1607,7 +1632,7 @@ main(int ac, char **av)
case 'o':
line = xstrdup(optarg);
if (process_server_config_line(&options, line,
- "command-line", 0, NULL, NULL) != 0)
+ "command-line", 0, NULL, NULL, &includes) != 0)
exit(1);
free(line);
break;
@@ -1619,22 +1644,16 @@ main(int ac, char **av)
}
if (rexeced_flag || inetd_flag)
rexec_flag = 0;
- if (!test_flag && (rexec_flag && (av[0] == NULL || *av[0] != '/')))
+ if (!test_flag && 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);
-#ifdef WITH_OPENSSL
- OpenSSL_add_all_algorithms();
-#endif
-
/* If requested, redirect the logs to the specified logfile. */
- if (logfile != NULL) {
+ if (logfile != NULL)
log_redirect_stderr_to(logfile);
- free(logfile);
- }
/*
* Force logging to stderr until we have loaded the private host
* key (unless started from inetd)
@@ -1644,7 +1663,7 @@ main(int ac, char **av)
SYSLOG_LEVEL_INFO : options.log_level,
options.log_facility == SYSLOG_FACILITY_NOT_SET ?
SYSLOG_FACILITY_AUTH : options.log_facility,
- log_stderr || !inetd_flag);
+ log_stderr || !inetd_flag || debug_flag);
/*
* Unset KRB5CCNAME, otherwise the user's session may inherit it from
@@ -1653,41 +1672,35 @@ main(int ac, char **av)
if (getenv("KRB5CCNAME") != NULL)
(void) unsetenv("KRB5CCNAME");
-#ifdef _UNICOS
- /* Cray can define user privs drop all privs now!
- * Not needed on PRIV_SU systems!
- */
- drop_cray_privs();
-#endif
-
- sensitive_data.server_key = NULL;
- sensitive_data.ssh1_host_key = NULL;
- sensitive_data.have_ssh1_key = 0;
sensitive_data.have_ssh2_key = 0;
/*
- * If we're doing an extended config test, make sure we have all of
- * the parameters we need. If we're not doing an extended test,
- * do not silently ignore connection test params.
+ * If we're not doing an extended test do not silently ignore connection
+ * test params.
*/
- if (test_flag >= 2 && server_match_spec_complete(connection_info) == 0)
- fatal("user, host and addr are all required when testing "
- "Match configs");
- if (test_flag < 2 && server_match_spec_complete(connection_info) >= 0)
+ if (test_flag < 2 && connection_info != NULL)
fatal("Config test connection parameter (-C) provided without "
"test mode (-T)");
/* Fetch our configuration */
- buffer_init(&cfg);
- if (rexeced_flag)
- recv_rexec_state(REEXEC_CONFIG_PASS_FD, &cfg);
- else
- load_server_config(config_file_name, &cfg);
+ if ((cfg = sshbuf_new()) == NULL)
+ fatal("%s: sshbuf_new failed", __func__);
+ if (rexeced_flag) {
+ 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)
+ load_server_config(config_file_name, cfg);
parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name,
- &cfg, NULL);
-
- seed_rng();
+ cfg, &includes, NULL);
/* Fill in default values for those options not explicitly set. */
fill_default_server_options(&options);
@@ -1702,6 +1715,11 @@ main(int ac, char **av)
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.
@@ -1710,22 +1728,16 @@ main(int ac, char **av)
* and warns for trivial misconfigurations that could break login.
*/
if (options.num_auth_methods != 0) {
- if ((options.protocol & SSH_PROTO_1))
- fatal("AuthenticationMethods is not supported with "
- "SSH protocol 1");
- for (n = 0; n < options.num_auth_methods; n++) {
- if (auth2_methods_valid(options.auth_methods[n],
+ for (i = 0; i < options.num_auth_methods; i++) {
+ if (auth2_methods_valid(options.auth_methods[i],
1) == 0)
break;
}
- if (n >= options.num_auth_methods)
+ if (i >= options.num_auth_methods)
fatal("AuthenticationMethods cannot be satisfied by "
"enabled authentication methods");
}
- /* set default channel AF */
- channel_set_af(options.address_family);
-
/* Check that there are no remaining arguments. */
if (optind < ac) {
fprintf(stderr, "Extra argument %s.\n", av[optind]);
@@ -1734,26 +1746,21 @@ main(int ac, char **av)
debug("sshd version %s, %s", SSH_VERSION,
#ifdef WITH_OPENSSL
- SSLeay_version(SSLEAY_VERSION)
+ OpenSSL_version(OPENSSL_VERSION)
#else
"without OpenSSL"
#endif
);
/* Store privilege separation user for later use if required. */
+ privsep_chroot = use_privsep && (getuid() == 0 || geteuid() == 0);
if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) {
- if (use_privsep || options.kerberos_authentication)
+ if (privsep_chroot || options.kerberos_authentication)
fatal("Privilege separation user %s does not exist",
SSH_PRIVSEP_USER);
} else {
- if (privsep_pw->pw_passwd != NULL) {
- explicit_bzero(privsep_pw->pw_passwd,
- strlen(privsep_pw->pw_passwd));
- }
privsep_pw = pwcopy(privsep_pw);
- if (privsep_pw->pw_passwd != NULL) {
- free(privsep_pw->pw_passwd);
- }
+ freezero(privsep_pw->pw_passwd, strlen(privsep_pw->pw_passwd));
privsep_pw->pw_passwd = xstrdup("*");
}
#if !defined(ANDROID)
@@ -1762,9 +1769,9 @@ main(int ac, char **av)
/* load host keys */
sensitive_data.host_keys = xcalloc(options.num_host_key_files,
- sizeof(Key *));
+ sizeof(struct sshkey *));
sensitive_data.host_pubkeys = xcalloc(options.num_host_key_files,
- sizeof(Key *));
+ sizeof(struct sshkey *));
if (options.host_key_agent) {
if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME))
@@ -1778,24 +1785,48 @@ main(int ac, char **av)
}
for (i = 0; i < options.num_host_key_files; i++) {
+ int ll = options.host_key_file_userprovided[i] ?
+ SYSLOG_LEVEL_ERROR : SYSLOG_LEVEL_DEBUG1;
+
if (options.host_key_files[i] == NULL)
continue;
- key = key_load_private(options.host_key_files[i], "", NULL);
- pubkey = key_load_public(options.host_key_files[i], NULL);
+ if ((r = sshkey_load_private(options.host_key_files[i], "",
+ &key, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR)
+ do_log2(ll, "Unable to load host key \"%s\": %s",
+ options.host_key_files[i], ssh_err(r));
+ if (sshkey_is_sk(key) &&
+ key->sk_flags & SSH_SK_USER_PRESENCE_REQD) {
+ debug("host key %s requires user presence, ignoring",
+ options.host_key_files[i]);
+ key->sk_flags &= ~SSH_SK_USER_PRESENCE_REQD;
+ }
+ if (r == 0 && key != NULL &&
+ (r = sshkey_shield_private(key)) != 0) {
+ do_log2(ll, "Unable to shield host key \"%s\": %s",
+ options.host_key_files[i], ssh_err(r));
+ sshkey_free(key);
+ key = NULL;
+ }
+ if ((r = sshkey_load_public(options.host_key_files[i],
+ &pubkey, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR)
+ do_log2(ll, "Unable to load host key \"%s\": %s",
+ options.host_key_files[i], ssh_err(r));
if (pubkey == NULL && key != NULL)
- pubkey = key_demote(key);
+ if ((r = sshkey_from_private(key, &pubkey)) != 0)
+ fatal("Could not demote key: \"%s\": %s",
+ options.host_key_files[i], ssh_err(r));
sensitive_data.host_keys[i] = key;
sensitive_data.host_pubkeys[i] = pubkey;
- if (key == NULL && pubkey != NULL && pubkey->type != KEY_RSA1 &&
- have_agent) {
+ if (key == NULL && pubkey != NULL && have_agent) {
debug("will rely on agent for hostkey %s",
options.host_key_files[i]);
keytype = pubkey->type;
} else if (key != NULL) {
keytype = key->type;
+ accumulate_host_timing_secret(cfg, key);
} else {
- error("Could not load host key: %s",
+ do_log2(ll, "Unable to load host key: %s",
options.host_key_files[i]);
sensitive_data.host_keys[i] = NULL;
sensitive_data.host_pubkeys[i] = NULL;
@@ -1803,14 +1834,13 @@ main(int ac, char **av)
}
switch (keytype) {
- case KEY_RSA1:
- sensitive_data.ssh1_host_key = key;
- sensitive_data.have_ssh1_key = 1;
- break;
case KEY_RSA:
case KEY_DSA:
case KEY_ECDSA:
case KEY_ED25519:
+ case KEY_ECDSA_SK:
+ case KEY_ED25519_SK:
+ case KEY_XMSS:
if (have_agent || key != NULL)
sensitive_data.have_ssh2_key = 1;
break;
@@ -1819,19 +1849,11 @@ main(int ac, char **av)
SSH_FP_DEFAULT)) == NULL)
fatal("sshkey_fingerprint failed");
debug("%s host key #%d: %s %s",
- key ? "private" : "agent", i, keytype == KEY_RSA1 ?
- sshkey_type(pubkey) : sshkey_ssh_name(pubkey), fp);
+ key ? "private" : "agent", i, sshkey_ssh_name(pubkey), fp);
free(fp);
}
- if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) {
- logit("Disabling protocol version 1. Could not load host key");
- options.protocol &= ~SSH_PROTO_1;
- }
- if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
- logit("Disabling protocol version 2. Could not load host key");
- options.protocol &= ~SSH_PROTO_2;
- }
- if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) {
+ accumulate_host_timing_secret(cfg, NULL);
+ if (!sensitive_data.have_ssh2_key) {
logit("sshd: no hostkeys available -- exiting.");
exit(1);
}
@@ -1841,28 +1863,28 @@ main(int ac, char **av)
* indices to the public keys that they relate to.
*/
sensitive_data.host_certificates = xcalloc(options.num_host_key_files,
- sizeof(Key *));
+ sizeof(struct sshkey *));
for (i = 0; i < options.num_host_key_files; i++)
sensitive_data.host_certificates[i] = NULL;
for (i = 0; i < options.num_host_cert_files; i++) {
if (options.host_cert_files[i] == NULL)
continue;
- key = key_load_public(options.host_cert_files[i], NULL);
- if (key == NULL) {
- error("Could not load host certificate: %s",
- options.host_cert_files[i]);
+ if ((r = sshkey_load_public(options.host_cert_files[i],
+ &key, NULL)) != 0) {
+ error("Could not load host certificate \"%s\": %s",
+ options.host_cert_files[i], ssh_err(r));
continue;
}
- if (!key_is_cert(key)) {
+ if (!sshkey_is_cert(key)) {
error("Certificate file is not a certificate: %s",
options.host_cert_files[i]);
- key_free(key);
+ sshkey_free(key);
continue;
}
/* Find matching private key */
for (j = 0; j < options.num_host_key_files; j++) {
- if (key_equal_public(key,
+ if (sshkey_equal_public(key,
sensitive_data.host_keys[j])) {
sensitive_data.host_certificates[j] = key;
break;
@@ -1871,42 +1893,15 @@ main(int ac, char **av)
if (j >= options.num_host_key_files) {
error("No matching private key for certificate: %s",
options.host_cert_files[i]);
- key_free(key);
+ sshkey_free(key);
continue;
}
sensitive_data.host_certificates[j] = key;
- debug("host certificate: #%d type %d %s", j, key->type,
- key_type(key));
- }
-
-#ifdef WITH_SSH1
- /* Check certain values for sanity. */
- if (options.protocol & SSH_PROTO_1) {
- if (options.server_key_bits < 512 ||
- options.server_key_bits > 32768) {
- fprintf(stderr, "Bad server key size.\n");
- exit(1);
- }
- /*
- * Check that server and host key lengths differ sufficiently. This
- * is necessary to make double encryption work with rsaref. Oh, I
- * hate software patents. I dont know if this can go? Niels
- */
- if (options.server_key_bits >
- BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) -
- SSH_KEY_BITS_RESERVED && options.server_key_bits <
- BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) +
- SSH_KEY_BITS_RESERVED) {
- options.server_key_bits =
- BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) +
- SSH_KEY_BITS_RESERVED;
- debug("Forcing server key to %d bits to make it differ from host key.",
- options.server_key_bits);
- }
+ debug("host certificate: #%u type %d %s", j, key->type,
+ sshkey_type(key));
}
-#endif
- if (use_privsep) {
+ if (privsep_chroot) {
struct stat st;
if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) ||
@@ -1926,8 +1921,14 @@ main(int ac, char **av)
}
if (test_flag > 1) {
- if (server_match_spec_complete(connection_info) == 1)
- parse_server_match_config(&options, 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);
}
@@ -1946,14 +1947,17 @@ main(int ac, char **av)
debug("setgroups() failed: %.200s", strerror(errno));
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 < rexec_argc; i++) {
+ 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;
}
+ listener_proctitle = prepare_proctitle(ac, av);
/* Ensure that umask disallows at least group and world write */
new_umask = umask(0077) | 0022;
@@ -1965,25 +1969,17 @@ main(int ac, char **av)
log_init(__progname, options.log_level, options.log_facility, log_stderr);
/*
- * If not in debugging mode, and not started from inetd, disconnect
- * from the controlling terminal, and fork. The original process
- * exits.
+ * If not in debugging mode, not started from inetd and not already
+ * daemonized (eg re-exec via SIGHUP), disconnect from the controlling
+ * terminal, and fork. The original process exits.
*/
- if (!(debug_flag || inetd_flag || no_daemon_flag)) {
-#ifdef TIOCNOTTY
- int fd;
-#endif /* TIOCNOTTY */
- if (daemon(0, 0) < 0)
+ already_daemon = daemonized();
+ if (!(debug_flag || inetd_flag || no_daemon_flag || already_daemon)) {
+
+ if (daemon(0, 0) == -1)
fatal("daemon() failed: %.200s", strerror(errno));
- /* Disconnect from the controlling tty. */
-#ifdef TIOCNOTTY
- fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
- if (fd >= 0) {
- (void) ioctl(fd, TIOCNOTTY, NULL);
- close(fd);
- }
-#endif /* TIOCNOTTY */
+ disconnect_controlling_tty();
}
/* Reinitialize the log (because of the fork above). */
log_init(__progname, options.log_level, options.log_facility, log_stderr);
@@ -1994,7 +1990,7 @@ main(int ac, char **av)
error("chdir(\"/\"): %s", strerror(errno));
/* ignore SIGPIPE */
- signal(SIGPIPE, SIG_IGN);
+ ssh_signal(SIGPIPE, SIG_IGN);
/* Get a connection, either from inetd or a listening TCP socket */
if (inetd_flag) {
@@ -2003,13 +1999,10 @@ main(int ac, char **av)
platform_pre_listen();
server_listen();
- if (options.protocol & SSH_PROTO_1)
- generate_ephemeral_server_key();
-
- signal(SIGHUP, sighup_handler);
- signal(SIGCHLD, main_sigchld_handler);
- signal(SIGTERM, sigterm_handler);
- signal(SIGQUIT, sigterm_handler);
+ ssh_signal(SIGHUP, sighup_handler);
+ ssh_signal(SIGCHLD, main_sigchld_handler);
+ ssh_signal(SIGTERM, sigterm_handler);
+ ssh_signal(SIGQUIT, sigterm_handler);
/*
* Write out the pid file after the sigterm handler
@@ -2046,7 +2039,7 @@ main(int ac, char **av)
* controlling terminal which will result in "could not set
* controlling tty" errors.
*/
- if (!debug_flag && !inetd_flag && setsid() < 0)
+ if (!debug_flag && !inetd_flag && setsid() == -1)
error("setsid: %.100s", strerror(errno));
#endif
@@ -2093,56 +2086,64 @@ main(int ac, char **av)
fcntl(sock_out, F_SETFD, FD_CLOEXEC);
fcntl(sock_in, F_SETFD, FD_CLOEXEC);
- /*
- * Disable the key regeneration alarm. We will not regenerate the
- * key since we are no longer in a position to give it to anyone. We
- * will not restart on SIGHUP since it no longer makes sense.
- */
- alarm(0);
- signal(SIGALRM, SIG_DFL);
- signal(SIGHUP, SIG_DFL);
- signal(SIGTERM, SIG_DFL);
- signal(SIGQUIT, SIG_DFL);
- signal(SIGCHLD, SIG_DFL);
- signal(SIGINT, SIG_DFL);
+ /* 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);
+ ssh_signal(SIGINT, SIG_DFL);
/*
* Register our connection. This turns encryption off because we do
* not have a key.
*/
- packet_set_connection(sock_in, sock_out);
- packet_set_server();
+ 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_permitopen(ssh, &options);
/* Set SO_KEEPALIVE if requested. */
- if (options.tcp_keep_alive && packet_connection_is_on_socket() &&
- setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0)
+ 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 = get_remote_port()) < 0) {
- debug("get_remote_port failed");
+ if ((remote_port = ssh_remote_port(ssh)) < 0) {
+ debug("ssh_remote_port failed");
cleanup_exit(255);
}
- /*
- * We use get_canonical_hostname with usedns = 0 instead of
- * get_remote_ipaddr here so IP options will be checked.
- */
- (void) get_canonical_hostname(0);
+ if (options.routing_domain != NULL)
+ set_process_rdomain(ssh, options.routing_domain);
+
/*
* The rest of the code depends on the fact that
- * get_remote_ipaddr() caches the remote ip, even if
+ * ssh_remote_ipaddr() caches the remote ip, even if
* the socket goes away.
*/
- remote_ip = get_remote_ipaddr();
+ remote_ip = ssh_remote_ipaddr(ssh);
#ifdef SSH_AUDIT_EVENTS
audit_connection_from(remote_ip, remote_port);
#endif
+ rdomain = ssh_packet_rdomain_in(ssh);
+
/* Log the connection. */
- verbose("Connection from %s port %d on %s port %d",
- remote_ip, remote_port,
- get_local_ipaddr(sock_in), get_local_port());
+ 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
@@ -2152,34 +2153,38 @@ main(int ac, char **av)
* mode; it is just annoying to have the server exit just when you
* are about to discover the bug.
*/
- signal(SIGALRM, grace_alarm_handler);
+ ssh_signal(SIGALRM, grace_alarm_handler);
if (!debug_flag)
alarm(options.login_grace_time);
- sshd_exchange_identification(sock_in, sock_out);
-
- /* In inetd mode, generate ephemeral key only for proto 1 connections */
- if (!compat20 && inetd_flag && sensitive_data.server_key == NULL)
- generate_ephemeral_server_key();
+ if ((r = kex_exchange_identification(ssh, -1,
+ options.version_addendum)) != 0)
+ sshpkt_fatal(ssh, r, "banner exchange");
- packet_set_nonblocking();
+ ssh_packet_set_nonblocking(ssh);
/* allocate authentication context */
authctxt = xcalloc(1, sizeof(*authctxt));
+ ssh->authctxt = authctxt;
- authctxt->loginmsg = &loginmsg;
+ authctxt->loginmsg = loginmsg;
/* 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 */
- buffer_init(&loginmsg);
+ if ((loginmsg = sshbuf_new()) == NULL)
+ fatal("%s: sshbuf_new failed", __func__);
auth_debug_reset();
if (use_privsep) {
- if (privsep_preauth(authctxt) == 1)
+ if (privsep_preauth(ssh) == 1)
goto authenticated;
- } else if (compat20 && have_agent) {
+ } else if (have_agent) {
if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) {
error("Unable to get agent socket: %s", ssh_err(r));
have_agent = 0;
@@ -2188,23 +2193,16 @@ main(int ac, char **av)
/* perform the key exchange */
/* authenticate user and start session */
- if (compat20) {
- do_ssh2_kex();
- do_authentication2(authctxt);
- } else {
-#ifdef WITH_SSH1
- do_ssh1_kex();
- do_authentication(authctxt);
-#else
- fatal("ssh1 not supported");
-#endif
- }
+ 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(pmonitor);
+ mm_send_keystate(ssh, pmonitor);
+ ssh_packet_clear_keys(ssh);
exit(0);
}
@@ -2214,7 +2212,7 @@ main(int ac, char **av)
* authentication.
*/
alarm(0);
- signal(SIGALRM, SIG_DFL);
+ ssh_signal(SIGALRM, SIG_DFL);
authctxt->authenticated = 1;
if (startup_pipe != -1) {
close(startup_pipe);
@@ -2222,7 +2220,7 @@ main(int ac, char **av)
}
#ifdef SSH_AUDIT_EVENTS
- audit_event(SSH_AUTH_SUCCESS);
+ audit_event(ssh, SSH_AUTH_SUCCESS);
#endif
#ifdef GSSAPI
@@ -2235,7 +2233,7 @@ main(int ac, char **av)
#ifdef USE_PAM
if (options.use_pam) {
do_pam_setcred(1);
- do_pam_session();
+ do_pam_session(ssh);
}
#endif
@@ -2244,24 +2242,21 @@ main(int ac, char **av)
* file descriptor passing.
*/
if (use_privsep) {
- privsep_postauth(authctxt);
+ privsep_postauth(ssh, authctxt);
/* the monitor process [priv] will not return */
- if (!compat20)
- destroy_sensitive_data();
}
- packet_set_timeout(options.client_alive_interval,
+ ssh_packet_set_timeout(ssh, options.client_alive_interval,
options.client_alive_count_max);
/* Try to send all our hostkeys to the client */
- if (compat20)
- notify_hostkeys(active_state);
+ notify_hostkeys(ssh);
/* Start session. */
- do_authenticated(authctxt);
+ do_authenticated(ssh, authctxt);
/* The connection has been terminated. */
- packet_get_bytes(&ibytes, &obytes);
+ ssh_packet_get_bytes(ssh, &ibytes, &obytes);
verbose("Transferred: sent %llu, received %llu bytes",
(unsigned long long)obytes, (unsigned long long)ibytes);
@@ -2273,10 +2268,10 @@ main(int ac, char **av)
#endif /* USE_PAM */
#ifdef SSH_AUDIT_EVENTS
- PRIVSEP(audit_event(SSH_CONNECTION_CLOSE));
+ PRIVSEP(audit_event(ssh, SSH_CONNECTION_CLOSE));
#endif
- packet_close();
+ ssh_packet_close(ssh);
if (use_privsep)
mm_terminate();
@@ -2284,318 +2279,95 @@ main(int ac, char **av)
exit(0);
}
-#ifdef WITH_SSH1
-/*
- * Decrypt session_key_int using our private server key and private host key
- * (key with larger modulus first).
- */
int
-ssh1_session_key(BIGNUM *session_key_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 rsafail = 0;
-
- if (BN_cmp(sensitive_data.server_key->rsa->n,
- sensitive_data.ssh1_host_key->rsa->n) > 0) {
- /* Server key has bigger modulus. */
- if (BN_num_bits(sensitive_data.server_key->rsa->n) <
- BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) +
- SSH_KEY_BITS_RESERVED) {
- fatal("do_connection: %s: "
- "server_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d",
- get_remote_ipaddr(),
- BN_num_bits(sensitive_data.server_key->rsa->n),
- BN_num_bits(sensitive_data.ssh1_host_key->rsa->n),
- SSH_KEY_BITS_RESERVED);
+ int r;
+
+ if (use_privsep) {
+ if (privkey) {
+ if (mm_sshkey_sign(ssh, privkey, signature, slenp,
+ data, dlen, alg, options.sk_provider,
+ ssh->compat) < 0)
+ fatal("%s: privkey sign failed", __func__);
+ } else {
+ if (mm_sshkey_sign(ssh, pubkey, signature, slenp,
+ data, dlen, alg, options.sk_provider,
+ ssh->compat) < 0)
+ fatal("%s: pubkey sign failed", __func__);
}
- if (rsa_private_decrypt(session_key_int, session_key_int,
- sensitive_data.server_key->rsa) != 0)
- rsafail++;
- if (rsa_private_decrypt(session_key_int, session_key_int,
- sensitive_data.ssh1_host_key->rsa) != 0)
- rsafail++;
} else {
- /* Host key has bigger modulus (or they are equal). */
- if (BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) <
- BN_num_bits(sensitive_data.server_key->rsa->n) +
- SSH_KEY_BITS_RESERVED) {
- fatal("do_connection: %s: "
- "host_key %d < server_key %d + SSH_KEY_BITS_RESERVED %d",
- get_remote_ipaddr(),
- BN_num_bits(sensitive_data.ssh1_host_key->rsa->n),
- BN_num_bits(sensitive_data.server_key->rsa->n),
- SSH_KEY_BITS_RESERVED);
+ if (privkey) {
+ if (sshkey_sign(privkey, signature, slenp, data, dlen,
+ alg, options.sk_provider, ssh->compat) < 0)
+ fatal("%s: privkey sign failed", __func__);
+ } else {
+ if ((r = ssh_agent_sign(auth_sock, pubkey,
+ signature, slenp, data, dlen, alg,
+ ssh->compat)) != 0) {
+ fatal("%s: agent sign failed: %s",
+ __func__, ssh_err(r));
+ }
}
- if (rsa_private_decrypt(session_key_int, session_key_int,
- sensitive_data.ssh1_host_key->rsa) != 0)
- rsafail++;
- if (rsa_private_decrypt(session_key_int, session_key_int,
- sensitive_data.server_key->rsa) != 0)
- rsafail++;
- }
- return (rsafail);
-}
-
-/*
- * SSH1 key exchange
- */
-static void
-do_ssh1_kex(void)
-{
- int i, len;
- int rsafail = 0;
- BIGNUM *session_key_int, *fake_key_int, *real_key_int;
- u_char session_key[SSH_SESSION_KEY_LENGTH];
- u_char fake_key_bytes[4096 / 8];
- size_t fake_key_len;
- u_char cookie[8];
- u_int cipher_type, auth_mask, protocol_flags;
-
- /*
- * Generate check bytes that the client must send back in the user
- * packet in order for it to be accepted; this is used to defy ip
- * spoofing attacks. Note that this only works against somebody
- * doing IP spoofing from a remote machine; any machine on the local
- * network can still see outgoing packets and catch the random
- * cookie. This only affects rhosts authentication, and this is one
- * of the reasons why it is inherently insecure.
- */
- arc4random_buf(cookie, sizeof(cookie));
-
- /*
- * Send our public key. We include in the packet 64 bits of random
- * data that must be matched in the reply in order to prevent IP
- * spoofing.
- */
- packet_start(SSH_SMSG_PUBLIC_KEY);
- for (i = 0; i < 8; i++)
- packet_put_char(cookie[i]);
-
- /* Store our public server RSA key. */
- packet_put_int(BN_num_bits(sensitive_data.server_key->rsa->n));
- packet_put_bignum(sensitive_data.server_key->rsa->e);
- packet_put_bignum(sensitive_data.server_key->rsa->n);
-
- /* Store our public host RSA key. */
- packet_put_int(BN_num_bits(sensitive_data.ssh1_host_key->rsa->n));
- packet_put_bignum(sensitive_data.ssh1_host_key->rsa->e);
- packet_put_bignum(sensitive_data.ssh1_host_key->rsa->n);
-
- /* Put protocol flags. */
- packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN);
-
- /* Declare which ciphers we support. */
- packet_put_int(cipher_mask_ssh1(0));
-
- /* Declare supported authentication types. */
- auth_mask = 0;
- if (options.rhosts_rsa_authentication)
- auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA;
- if (options.rsa_authentication)
- auth_mask |= 1 << SSH_AUTH_RSA;
- if (options.challenge_response_authentication == 1)
- auth_mask |= 1 << SSH_AUTH_TIS;
- if (options.password_authentication)
- auth_mask |= 1 << SSH_AUTH_PASSWORD;
- packet_put_int(auth_mask);
-
- /* Send the packet and wait for it to be sent. */
- packet_send();
- packet_write_wait();
-
- debug("Sent %d bit server key and %d bit host key.",
- BN_num_bits(sensitive_data.server_key->rsa->n),
- BN_num_bits(sensitive_data.ssh1_host_key->rsa->n));
-
- /* Read clients reply (cipher type and session key). */
- packet_read_expect(SSH_CMSG_SESSION_KEY);
-
- /* Get cipher type and check whether we accept this. */
- cipher_type = packet_get_char();
-
- if (!(cipher_mask_ssh1(0) & (1 << cipher_type)))
- packet_disconnect("Warning: client selects unsupported cipher.");
-
- /* Get check bytes from the packet. These must match those we
- sent earlier with the public key packet. */
- for (i = 0; i < 8; i++)
- if (cookie[i] != packet_get_char())
- packet_disconnect("IP Spoofing check bytes do not match.");
-
- debug("Encryption type: %.200s", cipher_name(cipher_type));
-
- /* Get the encrypted integer. */
- if ((real_key_int = BN_new()) == NULL)
- fatal("do_ssh1_kex: BN_new failed");
- packet_get_bignum(real_key_int);
-
- protocol_flags = packet_get_int();
- packet_set_protocol_flags(protocol_flags);
- packet_check_eom();
-
- /* Setup a fake key in case RSA decryption fails */
- if ((fake_key_int = BN_new()) == NULL)
- fatal("do_ssh1_kex: BN_new failed");
- fake_key_len = BN_num_bytes(real_key_int);
- if (fake_key_len > sizeof(fake_key_bytes))
- fake_key_len = sizeof(fake_key_bytes);
- arc4random_buf(fake_key_bytes, fake_key_len);
- if (BN_bin2bn(fake_key_bytes, fake_key_len, fake_key_int) == NULL)
- fatal("do_ssh1_kex: BN_bin2bn failed");
-
- /* Decrypt real_key_int using host/server keys */
- rsafail = PRIVSEP(ssh1_session_key(real_key_int));
- /* If decryption failed, use the fake key. Else, the real key. */
- if (rsafail)
- session_key_int = fake_key_int;
- else
- session_key_int = real_key_int;
-
- /*
- * Extract session key from the decrypted integer. The key is in the
- * least significant 256 bits of the integer; the first byte of the
- * key is in the highest bits.
- */
- (void) BN_mask_bits(session_key_int, sizeof(session_key) * 8);
- len = BN_num_bytes(session_key_int);
- if (len < 0 || (u_int)len > sizeof(session_key)) {
- error("do_ssh1_kex: bad session key len from %s: "
- "session_key_int %d > sizeof(session_key) %lu",
- get_remote_ipaddr(), len, (u_long)sizeof(session_key));
- rsafail++;
- } else {
- explicit_bzero(session_key, sizeof(session_key));
- BN_bn2bin(session_key_int,
- session_key + sizeof(session_key) - len);
-
- derive_ssh1_session_id(
- sensitive_data.ssh1_host_key->rsa->n,
- sensitive_data.server_key->rsa->n,
- cookie, session_id);
- /*
- * Xor the first 16 bytes of the session key with the
- * session id.
- */
- for (i = 0; i < 16; i++)
- session_key[i] ^= session_id[i];
- }
-
- /* Destroy the private and public keys. No longer. */
- destroy_sensitive_data();
-
- if (use_privsep)
- mm_ssh1_session_id(session_id);
-
- /* Destroy the decrypted integer. It is no longer needed. */
- BN_clear_free(real_key_int);
- BN_clear_free(fake_key_int);
-
- /* Set the session key. From this on all communications will be encrypted. */
- packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type);
-
- /* Destroy our copy of the session key. It is no longer needed. */
- explicit_bzero(session_key, sizeof(session_key));
-
- debug("Received session key; encryption turned on.");
-
- /* Send an acknowledgment packet. Note that this packet is sent encrypted. */
- packet_start(SSH_SMSG_SUCCESS);
- packet_send();
- packet_write_wait();
-}
-#endif
-
-int
-sshd_hostkey_sign(Key *privkey, Key *pubkey, u_char **signature, size_t *slen,
- const u_char *data, size_t dlen, u_int flag)
-{
- int r;
- u_int xxx_slen, xxx_dlen = dlen;
-
- if (privkey) {
- if (PRIVSEP(key_sign(privkey, signature, &xxx_slen, data, xxx_dlen) < 0))
- fatal("%s: key_sign failed", __func__);
- if (slen)
- *slen = xxx_slen;
- } else if (use_privsep) {
- if (mm_key_sign(pubkey, signature, &xxx_slen, data, xxx_dlen) < 0)
- fatal("%s: pubkey_sign failed", __func__);
- if (slen)
- *slen = xxx_slen;
- } else {
- if ((r = ssh_agent_sign(auth_sock, pubkey, signature, slen,
- data, dlen, datafellows)) != 0)
- fatal("%s: ssh_agent_sign failed: %s",
- __func__, ssh_err(r));
}
return 0;
}
-/*
- * SSH2 key exchange: diffie-hellman-group1-sha1
- */
+/* SSH2 key exchange */
static void
-do_ssh2_kex(void)
+do_ssh2_kex(struct ssh *ssh)
{
char *myproposal[PROPOSAL_MAX] = { KEX_SERVER };
struct kex *kex;
int r;
- if (options.ciphers != NULL) {
- myproposal[PROPOSAL_ENC_ALGS_CTOS] =
- myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
- }
- myproposal[PROPOSAL_ENC_ALGS_CTOS] =
- compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]);
- myproposal[PROPOSAL_ENC_ALGS_STOC] =
- compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]);
-
- if (options.macs != NULL) {
- myproposal[PROPOSAL_MAC_ALGS_CTOS] =
- myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
- }
+ myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(
+ options.kex_algorithms);
+ myproposal[PROPOSAL_ENC_ALGS_CTOS] = compat_cipher_proposal(
+ options.ciphers);
+ myproposal[PROPOSAL_ENC_ALGS_STOC] = compat_cipher_proposal(
+ options.ciphers);
+ myproposal[PROPOSAL_MAC_ALGS_CTOS] =
+ myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
+
if (options.compression == COMP_NONE) {
myproposal[PROPOSAL_COMP_ALGS_CTOS] =
- myproposal[PROPOSAL_COMP_ALGS_STOC] = "none";
- } else if (options.compression == COMP_DELAYED) {
- myproposal[PROPOSAL_COMP_ALGS_CTOS] =
- myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com";
+ myproposal[PROPOSAL_COMP_ALGS_STOC] = "none";
}
- if (options.kex_algorithms != NULL)
- myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
-
- myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(
- myproposal[PROPOSAL_KEX_ALGS]);
if (options.rekey_limit || options.rekey_interval)
- packet_set_rekey_limits((u_int32_t)options.rekey_limit,
- (time_t)options.rekey_interval);
+ ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
+ options.rekey_interval);
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
list_hostkey_types());
/* start key exchange */
- if ((r = kex_setup(active_state, myproposal)) != 0)
+ if ((r = kex_setup(ssh, myproposal)) != 0)
fatal("kex_setup: %s", ssh_err(r));
- kex = active_state->kex;
+ kex = ssh->kex;
#ifdef WITH_OPENSSL
- kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
- kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
+ 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] = kexecdh_server;
+ kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
# endif
#endif
- kex->kex[KEX_C25519_SHA256] = kexc25519_server;
- kex->server = 1;
- kex->client_version_string=client_version_string;
- kex->server_version_string=server_version_string;
+ kex->kex[KEX_C25519_SHA256] = kex_gen_server;
+ kex->kex[KEX_KEM_SNTRUP4591761X25519_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;
- dispatch_run(DISPATCH_BLOCK, &kex->done, active_state);
+ ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &kex->done);
session_id2 = kex->session_id;
session_id2_len = kex->session_id_len;
@@ -2614,8 +2386,8 @@ do_ssh2_kex(void)
void
cleanup_exit(int i)
{
- if (the_authctxt) {
- do_cleanup(the_authctxt);
+ 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);
@@ -2627,8 +2399,8 @@ cleanup_exit(int i)
}
#ifdef SSH_AUDIT_EVENTS
/* done after do_cleanup so it can cancel the PAM auth 'thread' */
- if (!use_privsep || mm_is_monitor())
- audit_event(SSH_CONNECTION_ABANDON);
+ if (the_active_state != NULL && (!use_privsep || mm_is_monitor()))
+ audit_event(the_active_state, SSH_CONNECTION_ABANDON);
#endif
_exit(i);
}