-/* $OpenBSD: sshd.c,v 1.385 2011/06/23 09:34:13 djm Exp $ */
+/* $OpenBSD: sshd.c,v 1.444 2015/02/20 22:17:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <limits.h>
+#ifdef WITH_OPENSSL
#include <openssl/dh.h>
#include <openssl/bn.h>
-#include <openssl/md5.h>
#include <openssl/rand.h>
#include "openbsd-compat/openssl-compat.h"
+#endif
#ifdef HAVE_SECUREWARE
#include <sys/security.h>
#include "packet.h"
#include "log.h"
#include "buffer.h"
+#include "misc.h"
#include "servconf.h"
#include "uidswap.h"
#include "compat.h"
#include "cipher.h"
+#include "digest.h"
#include "key.h"
#include "kex.h"
-#include "dh.h"
#include "myproposal.h"
#include "authfile.h"
#include "pathnames.h"
#include "canohost.h"
#include "hostfile.h"
#include "auth.h"
-#include "misc.h"
+#include "authfd.h"
#include "msg.h"
#include "dispatch.h"
#include "channels.h"
#include "roaming.h"
#include "ssh-sandbox.h"
#include "version.h"
-
-#ifdef LIBWRAP
-#include <tcpd.h>
-#include <syslog.h>
-int allow_severity;
-int deny_severity;
-#endif /* LIBWRAP */
+#include "ssherr.h"
#ifndef O_NOCTTY
#define O_NOCTTY 0
char *client_version_string = NULL;
char *server_version_string = NULL;
-/* for rekeying XXX fixme */
-Kex *xxx_kex;
+/* Daemon's agent connection */
+int auth_sock = -1;
+int have_agent = 0;
/*
* Any really sensitive data in the application is contained in this
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_int session_id2_len = 0;
/* record remote hostname or ip */
-u_int utmp_len = MAXHOSTNAMELEN;
+u_int utmp_len = HOST_NAME_MAX+1;
/* options.max_startup sized array of fd ints */
int *startup_pipes = NULL;
/* variables used for privilege separation */
int use_privsep = -1;
struct monitor *pmonitor = NULL;
+int privsep_is_preauth = 1;
/* global authentication context */
Authctxt *the_authctxt = NULL;
void destroy_sensitive_data(void);
void demote_sensitive_data(void);
+#ifdef WITH_SSH1
static void do_ssh1_kex(void);
+#endif
static void do_ssh2_kex(void);
/*
sighup_restart(void)
{
logit("Received SIGHUP; restarting.");
+ platform_pre_restart();
close_listen_socks();
close_startup_pipes();
alarm(0); /* alarm timer persists across exec */
if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0)
kill(pmonitor->m_pid, SIGALRM);
+ /*
+ * Try to kill any processes that we have spawned, E.g. authorized
+ * keys command helpers.
+ */
+ if (getpgid(0) == getpid()) {
+ signal(SIGTERM, SIG_IGN);
+ kill(0, SIGTERM);
+ }
+
/* Log error and exit. */
sigdie("Timeout before authentication for %s", get_remote_ipaddr());
}
verbose("RSA key generation complete.");
arc4random_buf(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH);
- arc4random_stir();
}
/*ARGSUSED*/
major = PROTOCOL_MAJOR_1;
minor = PROTOCOL_MINOR_1;
}
- snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", major, minor,
- SSH_VERSION, newline);
- server_version_string = xstrdup(buf);
+
+ 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,
&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);
- logit("Bad protocol version identification '%.100s' from %s",
- client_version_string, get_remote_ipaddr());
cleanup_exit(255);
}
debug("Client protocol version %d.%d; client software version %.100s",
remote_major, remote_minor, remote_version);
- compat_datafellows(remote_version);
+ active_state->compat = compat_datafellows(remote_version);
- if (datafellows & SSH_BUG_PROBE) {
+ 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) {
+ 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) {
}
}
sensitive_data.ssh1_host_key = NULL;
- memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH);
+ explicit_bzero(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH);
}
/* Demote private to public keys for network child */
/* Enable challenge-response authentication for privilege separation */
privsep_challenge_enable();
+#ifdef GSSAPI
+ /* Cache supported mechanism OIDs for later use */
+ if (options.gss_authentication)
+ ssh_gssapi_prepare_supported_oids();
+#endif
+
arc4random_stir();
arc4random_buf(rnd, sizeof(rnd));
+#ifdef WITH_OPENSSL
RAND_seed(rnd, sizeof(rnd));
+#endif
+ explicit_bzero(rnd, sizeof(rnd));
/* Demote the private keys to public keys. */
demote_sensitive_data();
static int
privsep_preauth(Authctxt *authctxt)
{
- int status;
+ 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 = &xxx_kex;
+ pmonitor->m_pkex = &active_state->kex;
- if (use_privsep == PRIVSEP_SANDBOX)
- box = ssh_sandbox_init();
+ if (use_privsep == PRIVSEP_ON)
+ box = ssh_sandbox_init(pmonitor);
pid = fork();
if (pid == -1) {
fatal("fork of unprivileged child failed");
} else if (pid != 0) {
debug2("Network child is on pid %ld", (long)pid);
+ pmonitor->m_pid = pid;
+ if (have_agent) {
+ r = ssh_get_authentication_socket(&auth_sock);
+ if (r != 0) {
+ error("Could not get agent socket: %s",
+ ssh_err(r));
+ have_agent = 0;
+ }
+ }
if (box != NULL)
ssh_sandbox_parent_preauth(box, pid);
- pmonitor->m_pid = pid;
monitor_child_preauth(authctxt, pmonitor);
/* Sync memory */
/* Wait for the child's exit status */
while (waitpid(pid, &status, 0) < 0) {
- if (errno != EINTR)
- fatal("%s: waitpid: %s", __func__,
- strerror(errno));
+ if (errno == EINTR)
+ continue;
+ pmonitor->m_pid = -1;
+ fatal("%s: waitpid: %s", __func__, strerror(errno));
}
+ privsep_is_preauth = 0;
+ pmonitor->m_pid = -1;
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) != 0)
fatal("%s: preauth child exited with status %d",
arc4random_stir();
arc4random_buf(rnd, sizeof(rnd));
+#ifdef WITH_OPENSSL
RAND_seed(rnd, sizeof(rnd));
+#endif
+ explicit_bzero(rnd, sizeof(rnd));
/* Drop privileges */
do_setusercontext(authctxt->pw);
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:
case KEY_DSA:
case KEY_ECDSA:
+ case KEY_ED25519:
if (buffer_len(&b) > 0)
buffer_append(&b, ",", 1);
p = key_ssh_name(key);
case KEY_RSA_CERT:
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);
}
static Key *
-get_hostkey_by_type(int type, int need_private)
+get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
{
int i;
Key *key;
case KEY_RSA_CERT:
case KEY_DSA_CERT:
case KEY_ECDSA_CERT:
+ case KEY_ED25519_CERT:
key = sensitive_data.host_certificates[i];
break;
default:
key = sensitive_data.host_keys[i];
+ if (key == NULL && !need_private)
+ key = sensitive_data.host_pubkeys[i];
break;
}
- if (key != NULL && key->type == type)
+ if (key != NULL && key->type == type &&
+ (key->type != KEY_ECDSA || key->ecdsa_nid == nid))
return need_private ?
sensitive_data.host_keys[i] : key;
}
}
Key *
-get_hostkey_public_by_type(int type)
+get_hostkey_public_by_type(int type, int nid, struct ssh *ssh)
{
- return get_hostkey_by_type(type, 0);
+ return get_hostkey_by_type(type, nid, 0, ssh);
}
Key *
-get_hostkey_private_by_type(int type)
+get_hostkey_private_by_type(int type, int nid, struct ssh *ssh)
{
- return get_hostkey_by_type(type, 1);
+ return get_hostkey_by_type(type, nid, 1, ssh);
}
Key *
return (sensitive_data.host_keys[ind]);
}
+Key *
+get_hostkey_public_by_index(int ind, struct ssh *ssh)
+{
+ if (ind < 0 || ind >= options.num_host_key_files)
+ return (NULL);
+ return (sensitive_data.host_pubkeys[ind]);
+}
+
int
-get_hostkey_index(Key *key)
+get_hostkey_index(Key *key, int compare, struct ssh *ssh)
{
int i;
for (i = 0; i < options.num_host_key_files; i++) {
if (key_is_cert(key)) {
- if (key == sensitive_data.host_certificates[i])
+ if (key == sensitive_data.host_certificates[i] ||
+ (compare && sensitive_data.host_certificates[i] &&
+ sshkey_equal(key,
+ sensitive_data.host_certificates[i])))
return (i);
} else {
- if (key == sensitive_data.host_keys[i])
+ if (key == sensitive_data.host_keys[i] ||
+ (compare && sensitive_data.host_keys[i] &&
+ sshkey_equal(key, sensitive_data.host_keys[i])))
+ return (i);
+ if (key == sensitive_data.host_pubkeys[i] ||
+ (compare && sensitive_data.host_pubkeys[i] &&
+ sshkey_equal(key, sensitive_data.host_pubkeys[i])))
return (i);
}
}
return (-1);
}
+/* Inform the client of all hostkeys */
+static void
+notify_hostkeys(struct ssh *ssh)
+{
+ struct sshbuf *buf;
+ struct sshkey *key;
+ int i, nkeys, r;
+ char *fp;
+
+ 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))
+ continue;
+ fp = sshkey_fingerprint(key, options.fingerprint_hash,
+ SSH_FP_DEFAULT);
+ debug3("%s: key %d: %s %s", __func__, i,
+ 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 */
+ }
+ 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));
+ nkeys++;
+ }
+ debug3("%s: sent %d hostkeys", __func__, nkeys);
+ if (nkeys == 0)
+ fatal("%s: no hostkeys", __func__);
+ packet_send();
+ sshbuf_free(buf);
+}
+
/*
* returns 1 if connection should be dropped, 0 otherwise.
* dropping starts at connection #max_startups_begin with a probability
usage(void)
{
fprintf(stderr, "%s, %s\n",
- SSH_RELEASE, SSLeay_version(SSLEAY_VERSION));
+ SSH_RELEASE,
+#ifdef WITH_OPENSSL
+ SSLeay_version(SSLEAY_VERSION)
+#else
+ "without OpenSSL"
+#endif
+ );
fprintf(stderr,
"usage: sshd [-46DdeiqTt] [-b bits] [-C connection_spec] [-c host_cert_file]\n"
-" [-f config_file] [-g login_grace_time] [-h host_key_file]\n"
-" [-k key_gen_time] [-o option] [-p port] [-u len]\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"
);
exit(1);
}
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->p);
buffer_put_bignum(&m, sensitive_data.server_key->rsa->q);
} else
+#endif
buffer_put_int(&m, 0);
-#ifndef OPENSSL_PRNG_ONLY
+#if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY)
rexec_send_rng_seed(&m);
#endif
cp = buffer_get_string(&m, &len);
if (conf != NULL)
buffer_append(conf, cp, len + 1);
- xfree(cp);
+ 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->iqmp);
buffer_get_bignum(&m, sensitive_data.server_key->rsa->p);
buffer_get_bignum(&m, sensitive_data.server_key->rsa->q);
- rsa_generate_additional_parameters(
- sensitive_data.server_key->rsa);
+ 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
}
-#ifndef OPENSSL_PRNG_ONLY
+#if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY)
rexec_recv_rng_seed(&m);
#endif
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
- if (fd > STDOUT_FILENO)
+ if (!log_stderr)
+ dup2(fd, STDERR_FILENO);
+ if (fd > (log_stderr ? STDERR_FILENO : STDOUT_FILENO))
close(fd);
}
debug("inetd sockets after dupping: %d, %d", *sock_in, *sock_out);
struct sockaddr_storage from;
socklen_t fromlen;
pid_t pid;
+ u_char rnd[256];
/* setup fd set for accept */
fdset = NULL;
if (received_sighup)
sighup_restart();
if (fdset != NULL)
- xfree(fdset);
+ free(fdset);
fdset = (fd_set *)xcalloc(howmany(maxfd + 1, NFDBITS),
sizeof(fd_mask));
logit("Received signal %d; terminating.",
(int) received_sigterm);
close_listen_socks();
- unlink(options.pid_file);
+ if (options.pid_file != NULL)
+ unlink(options.pid_file);
exit(received_sigterm == SIGTERM ? 0 : 255);
}
if (key_used && key_do_regen) {
*newsock = accept(listen_socks[i],
(struct sockaddr *)&from, &fromlen);
if (*newsock < 0) {
- if (errno != EINTR && errno != EAGAIN &&
- errno != EWOULDBLOCK)
- error("accept: %.100s", strerror(errno));
+ if (errno != EINTR && errno != EWOULDBLOCK &&
+ errno != ECONNABORTED && errno != EAGAIN)
+ error("accept: %.100s",
+ strerror(errno));
+ if (errno == EMFILE || errno == ENFILE)
+ usleep(100 * 1000);
continue;
}
if (unset_nonblock(*newsock) == -1) {
* from that of the child
*/
arc4random_stir();
+ arc4random_buf(rnd, sizeof(rnd));
+#ifdef WITH_OPENSSL
+ RAND_seed(rnd, sizeof(rnd));
+#endif
+ explicit_bzero(rnd, sizeof(rnd));
}
/* child process check (or debug mode) */
{
extern char *optarg;
extern int optind;
- int opt, i, j, on = 1;
+ int r, opt, i, j, on = 1;
int sock_in = -1, sock_out = -1, newsock = -1;
const char *remote_ip;
- char *test_user = NULL, *test_host = NULL, *test_addr = NULL;
int remote_port;
- char *line, *p, *cp;
+ char *fp, *line, *logfile = NULL;
int config_s[2] = { -1 , -1 };
+ u_int n;
u_int64_t ibytes, obytes;
mode_t new_umask;
Key *key;
+ Key *pubkey;
+ int keytype;
Authctxt *authctxt;
+ struct connection_info *connection_info = get_connection_info(0, 0);
#ifdef HAVE_SECUREWARE
(void)set_auth_parameters(ac, av);
initialize_server_options(&options);
/* Parse command-line arguments. */
- while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:C:dDeiqrtQRT46")) != -1) {
+ while ((opt = getopt(ac, av, "f:p:b:k:h:g:u:o:C:dDeE:iqrtQRT46")) != -1) {
switch (opt) {
case '4':
options.address_family = AF_INET;
case 'D':
no_daemon_flag = 1;
break;
+ case 'E':
+ logfile = xstrdup(optarg);
+ /* FALLTHROUGH */
case 'e':
log_stderr = 1;
break;
test_flag = 2;
break;
case 'C':
- cp = optarg;
- while ((p = strsep(&cp, ",")) && *p != '\0') {
- if (strncmp(p, "addr=", 5) == 0)
- test_addr = xstrdup(p + 5);
- else if (strncmp(p, "host=", 5) == 0)
- test_host = xstrdup(p + 5);
- else if (strncmp(p, "user=", 5) == 0)
- test_user = xstrdup(p + 5);
- else {
- fprintf(stderr, "Invalid test "
- "mode specification %s\n", p);
- exit(1);
- }
- }
+ if (parse_server_match_testspec(connection_info,
+ optarg) == -1)
+ exit(1);
break;
case 'u':
- utmp_len = (u_int)strtonum(optarg, 0, MAXHOSTNAMELEN+1, NULL);
- if (utmp_len > MAXHOSTNAMELEN) {
+ utmp_len = (u_int)strtonum(optarg, 0, HOST_NAME_MAX+1+1, NULL);
+ if (utmp_len > HOST_NAME_MAX+1) {
fprintf(stderr, "Invalid utmp length.\n");
exit(1);
}
case 'o':
line = xstrdup(optarg);
if (process_server_config_line(&options, line,
- "command-line", 0, NULL, NULL, NULL, NULL) != 0)
+ "command-line", 0, NULL, NULL) != 0)
exit(1);
- xfree(line);
+ free(line);
break;
case '?':
default:
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) {
+ log_redirect_stderr_to(logfile);
+ free(logfile);
+ }
/*
* Force logging to stderr until we have loaded the private host
* key (unless started from inetd)
* root's environment
*/
if (getenv("KRB5CCNAME") != NULL)
- unsetenv("KRB5CCNAME");
+ (void) unsetenv("KRB5CCNAME");
#ifdef _UNICOS
/* Cray can define user privs drop all privs now!
* the parameters we need. If we're not doing an extended test,
* do not silently ignore connection test params.
*/
- if (test_flag >= 2 &&
- (test_user != NULL || test_host != NULL || test_addr != NULL)
- && (test_user == NULL || test_host == NULL || test_addr == NULL))
+ 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 && (test_user != NULL || test_host != NULL ||
- test_addr != NULL))
+ if (test_flag < 2 && server_match_spec_complete(connection_info) >= 0)
fatal("Config test connection parameter (-C) provided without "
"test mode (-T)");
load_server_config(config_file_name, &cfg);
parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name,
- &cfg, NULL, NULL, NULL);
+ &cfg, NULL);
seed_rng();
if (options.challenge_response_authentication)
options.kbd_interactive_authentication = 1;
+ /* Check that options are sensible */
+ if (options.authorized_keys_command_user == NULL &&
+ (options.authorized_keys_command != NULL &&
+ strcasecmp(options.authorized_keys_command, "none") != 0))
+ fatal("AuthorizedKeysCommand set without "
+ "AuthorizedKeysCommandUser");
+
+ /*
+ * Check whether there is any path through configured auth methods.
+ * Unfortunately it is not possible to verify this generally before
+ * daemonisation in the presence of Match block, but this catches
+ * and warns for trivial misconfigurations that could break login.
+ */
+ if (options.num_auth_methods != 0) {
+ 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],
+ 1) == 0)
+ break;
+ }
+ if (n >= options.num_auth_methods)
+ fatal("AuthenticationMethods cannot be satisfied by "
+ "enabled authentication methods");
+ }
+
/* set default channel AF */
channel_set_af(options.address_family);
exit(1);
}
- debug("sshd version %.100s", SSH_RELEASE);
+ debug("sshd version %s, %s", SSH_VERSION,
+#ifdef WITH_OPENSSL
+ SSLeay_version(SSLEAY_VERSION)
+#else
+ "without OpenSSL"
+#endif
+ );
/* Store privilege separation user for later use if required. */
if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) {
fatal("Privilege separation user %s does not exist",
SSH_PRIVSEP_USER);
} else {
- if (privsep_pw->pw_passwd)
- memset(privsep_pw->pw_passwd, 0, strlen(privsep_pw->pw_passwd));
+ 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)
- xfree(privsep_pw->pw_passwd);
+ if (privsep_pw->pw_passwd != NULL) {
+ free(privsep_pw->pw_passwd);
+ }
privsep_pw->pw_passwd = xstrdup("*");
}
+#if !defined(ANDROID)
endpwent();
+#endif
- /* load private host keys */
+ /* load host keys */
sensitive_data.host_keys = xcalloc(options.num_host_key_files,
sizeof(Key *));
- for (i = 0; i < options.num_host_key_files; i++)
- sensitive_data.host_keys[i] = NULL;
+ sensitive_data.host_pubkeys = xcalloc(options.num_host_key_files,
+ sizeof(Key *));
+
+ if (options.host_key_agent) {
+ if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME))
+ setenv(SSH_AUTHSOCKET_ENV_NAME,
+ options.host_key_agent, 1);
+ if ((r = ssh_get_authentication_socket(NULL)) == 0)
+ have_agent = 1;
+ else
+ error("Could not connect to agent \"%s\": %s",
+ options.host_key_agent, ssh_err(r));
+ }
for (i = 0; i < options.num_host_key_files; i++) {
+ 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 (pubkey == NULL && key != NULL)
+ pubkey = key_demote(key);
sensitive_data.host_keys[i] = key;
- if (key == NULL) {
+ sensitive_data.host_pubkeys[i] = pubkey;
+
+ if (key == NULL && pubkey != NULL && pubkey->type != KEY_RSA1 &&
+ 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;
+ } else {
error("Could not load host key: %s",
options.host_key_files[i]);
sensitive_data.host_keys[i] = NULL;
+ sensitive_data.host_pubkeys[i] = NULL;
continue;
}
- switch (key->type) {
+
+ switch (keytype) {
case KEY_RSA1:
sensitive_data.ssh1_host_key = key;
sensitive_data.have_ssh1_key = 1;
case KEY_RSA:
case KEY_DSA:
case KEY_ECDSA:
- sensitive_data.have_ssh2_key = 1;
+ case KEY_ED25519:
+ if (have_agent || key != NULL)
+ sensitive_data.have_ssh2_key = 1;
break;
}
- debug("private host key: #%d type %d %s", i, key->type,
- key_type(key));
+ if ((fp = sshkey_fingerprint(pubkey, options.fingerprint_hash,
+ SSH_FP_DEFAULT)) == NULL)
+ fatal("sshkey_fingerprint failed");
+ debug("%s host key #%d: %s %s",
+ key ? "private" : "agent", i, keytype == KEY_RSA1 ?
+ sshkey_type(pubkey) : 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");
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",
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);
}
}
+#endif
if (use_privsep) {
struct stat st;
}
if (test_flag > 1) {
- if (test_user != NULL && test_addr != NULL && test_host != NULL)
- parse_server_match_config(&options, test_user,
- test_host, test_addr);
+ if (server_match_spec_complete(connection_info) == 1)
+ parse_server_match_config(&options, connection_info);
dump_config(&options);
}
/* Reinitialize the log (because of the fork above). */
log_init(__progname, options.log_level, options.log_facility, log_stderr);
- /* Initialize the random number generator. */
- arc4random_stir();
-
/* Chdir to the root directory so that the current disk can be
unmounted if desired. */
- chdir("/");
+ if (chdir("/") == -1)
+ error("chdir(\"/\"): %s", strerror(errno));
/* ignore SIGPIPE */
signal(SIGPIPE, SIG_IGN);
* Write out the pid file after the sigterm handler
* is setup and the listen sockets are bound
*/
- if (!debug_flag) {
+ if (options.pid_file != NULL && !debug_flag) {
FILE *f = fopen(options.pid_file, "w");
if (f == NULL) {
dup2(STDIN_FILENO, STDOUT_FILENO);
if (startup_pipe == -1)
close(REEXEC_STARTUP_PIPE_FD);
- else
+ else if (startup_pipe != REEXEC_STARTUP_PIPE_FD) {
dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD);
+ close(startup_pipe);
+ startup_pipe = REEXEC_STARTUP_PIPE_FD;
+ }
dup2(config_s[1], REEXEC_CONFIG_PASS_FD);
close(config_s[1]);
- if (startup_pipe != -1)
- close(startup_pipe);
execv(rexec_argv[0], rexec_argv);
options.log_facility, log_stderr);
/* Clean up fds */
- startup_pipe = REEXEC_STARTUP_PIPE_FD;
- close(config_s[1]);
close(REEXEC_CONFIG_PASS_FD);
newsock = sock_out = sock_in = dup(STDIN_FILENO);
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
#ifdef SSH_AUDIT_EVENTS
audit_connection_from(remote_ip, remote_port);
#endif
-#ifdef LIBWRAP
- allow_severity = options.log_facility|LOG_INFO;
- deny_severity = options.log_facility|LOG_WARNING;
- /* Check whether logins are denied from this host. */
- if (packet_connection_is_on_socket()) {
- struct request_info req;
-
- request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, 0);
- fromhost(&req);
-
- if (!hosts_access(&req)) {
- debug("Connection refused by tcp wrapper");
- refuse(&req);
- /* NOTREACHED */
- fatal("libwrap refuse returns");
- }
- }
-#endif /* LIBWRAP */
/* Log the connection. */
- verbose("Connection from %.500s port %d", remote_ip, remote_port);
+ verbose("Connection from %s port %d on %s port %d",
+ remote_ip, remote_port,
+ get_local_ipaddr(sock_in), get_local_port());
/*
* We don't want to listen forever unless the other side
buffer_init(&loginmsg);
auth_debug_reset();
- if (use_privsep)
+ if (use_privsep) {
if (privsep_preauth(authctxt) == 1)
goto authenticated;
+ } else if (compat20 && have_agent) {
+ if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) {
+ error("Unable to get agent socket: %s", ssh_err(r));
+ have_agent = 0;
+ }
+ }
/* perform the key exchange */
/* authenticate user and start session */
do_ssh2_kex();
do_authentication2(authctxt);
} else {
+#ifdef WITH_SSH1
do_ssh1_kex();
do_authentication(authctxt);
+#else
+ fatal("ssh1 not supported");
+#endif
}
/*
* If we use privilege separation, the unprivileged child transfers
packet_set_timeout(options.client_alive_interval,
options.client_alive_count_max);
+ /* Try to send all our hostkeys to the client */
+ if (compat20)
+ notify_hostkeys(active_state);
+
/* Start session. */
do_authenticated(authctxt);
/* The connection has been terminated. */
- packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes);
- packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes);
+ packet_get_bytes(&ibytes, &obytes);
verbose("Transferred: sent %llu, received %llu bytes",
(unsigned long long)obytes, (unsigned long long)ibytes);
exit(0);
}
+#ifdef WITH_SSH1
/*
* Decrypt session_key_int using our private server key and private host key
* (key with larger modulus first).
SSH_KEY_BITS_RESERVED);
}
if (rsa_private_decrypt(session_key_int, session_key_int,
- sensitive_data.server_key->rsa) <= 0)
+ sensitive_data.server_key->rsa) != 0)
rsafail++;
if (rsa_private_decrypt(session_key_int, session_key_int,
- sensitive_data.ssh1_host_key->rsa) <= 0)
+ sensitive_data.ssh1_host_key->rsa) != 0)
rsafail++;
} else {
/* Host key has bigger modulus (or they are equal). */
SSH_KEY_BITS_RESERVED);
}
if (rsa_private_decrypt(session_key_int, session_key_int,
- sensitive_data.ssh1_host_key->rsa) < 0)
+ sensitive_data.ssh1_host_key->rsa) != 0)
rsafail++;
if (rsa_private_decrypt(session_key_int, session_key_int,
- sensitive_data.server_key->rsa) < 0)
+ sensitive_data.server_key->rsa) != 0)
rsafail++;
}
return (rsafail);
}
+
/*
* SSH1 key exchange
*/
{
int i, len;
int rsafail = 0;
- BIGNUM *session_key_int;
+ 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;
debug("Encryption type: %.200s", cipher_name(cipher_type));
/* Get the encrypted integer. */
- if ((session_key_int = BN_new()) == NULL)
+ if ((real_key_int = BN_new()) == NULL)
fatal("do_ssh1_kex: BN_new failed");
- packet_get_bignum(session_key_int);
+ packet_get_bignum(real_key_int);
protocol_flags = packet_get_int();
packet_set_protocol_flags(protocol_flags);
packet_check_eom();
- /* Decrypt session_key_int using host/server keys */
- rsafail = PRIVSEP(ssh1_session_key(session_key_int));
+ /* 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.
*/
- if (!rsafail) {
- (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 {
- memset(session_key, 0, 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];
- }
- }
- if (rsafail) {
- int bytes = BN_num_bytes(session_key_int);
- u_char *buf = xmalloc(bytes);
- MD5_CTX md;
-
- logit("do_connection: generating a fake encryption key");
- BN_bn2bin(session_key_int, buf);
- MD5_Init(&md);
- MD5_Update(&md, buf, bytes);
- MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH);
- MD5_Final(session_key, &md);
- MD5_Init(&md);
- MD5_Update(&md, session_key, 16);
- MD5_Update(&md, buf, bytes);
- MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH);
- MD5_Final(session_key + 16, &md);
- memset(buf, 0, bytes);
- xfree(buf);
+ (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_id[i] = session_key[i] ^ session_key[i + 16];
+ session_key[i] ^= session_id[i];
}
+
/* Destroy the private and public keys. No longer. */
destroy_sensitive_data();
mm_ssh1_session_id(session_id);
/* Destroy the decrypted integer. It is no longer needed. */
- BN_clear_free(session_key_int);
+ 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. */
- memset(session_key, 0, sizeof(session_key));
+ explicit_bzero(session_key, sizeof(session_key));
debug("Received session key; encryption turned on.");
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
static void
do_ssh2_kex(void)
{
- Kex *kex;
+ char *myproposal[PROPOSAL_MAX] = { KEX_SERVER };
+ struct kex *kex;
+ int r;
if (options.ciphers != NULL) {
myproposal[PROPOSAL_ENC_ALGS_CTOS] =
if (options.kex_algorithms != NULL)
myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
- myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types();
+ 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);
+
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(
+ list_hostkey_types());
/* start key exchange */
- kex = kex_setup(myproposal);
+ if ((r = kex_setup(active_state, myproposal)) != 0)
+ fatal("kex_setup: %s", ssh_err(r));
+ kex = active_state->kex;
+#ifdef WITH_OPENSSL
kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
kex->kex[KEX_DH_GRP14_SHA1] = kexdh_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;
+# 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->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;
- xxx_kex = kex;
-
- dispatch_run(DISPATCH_BLOCK, &kex->done, kex);
+ dispatch_run(DISPATCH_BLOCK, &kex->done, active_state);
session_id2 = kex->session_id;
session_id2_len = kex->session_id_len;
void
cleanup_exit(int i)
{
- if (the_authctxt)
+ if (the_authctxt) {
do_cleanup(the_authctxt);
+ if (use_privsep && privsep_is_preauth &&
+ pmonitor != NULL && pmonitor->m_pid > 1) {
+ debug("Killing privsep child %d", pmonitor->m_pid);
+ if (kill(pmonitor->m_pid, SIGKILL) != 0 &&
+ errno != ESRCH)
+ error("%s: kill(%d): %s", __func__,
+ pmonitor->m_pid, strerror(errno));
+ }
+ }
#ifdef SSH_AUDIT_EVENTS
/* done after do_cleanup so it can cancel the PAM auth 'thread' */
if (!use_privsep || mm_is_monitor())