freebsd-src/crypto/openssh/sshd.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

2567 lines
69 KiB
C
Raw Normal View History

2023-12-18 15:59:40 +00:00
/* $OpenBSD: sshd.c,v 1.601 2023/12/18 14:45:49 djm Exp $ */
2000-02-24 14:29:47 +00:00
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
* This program is the ssh daemon. It listens for connections from clients,
* and performs authentication, executes use commands or shell, and forwards
2000-02-24 14:29:47 +00:00
* information to/from the application to the user client over an encrypted
* connection. This can also handle forwarding of X11, TCP/IP, and
* authentication agent connections.
*
* As far as I am concerned, the code I have written for this software
* can be used freely for any purpose. Any derived versions of this
* software must be clearly marked as such, and if the derived work is
* incompatible with the protocol description in the RFC file, it must be
* called by a name other than "ssh" or "Secure Shell".
*
* SSH2 implementation:
* Privilege Separation:
*
* Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved.
* Copyright (c) 2002 Niels Provos. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2000-02-24 14:29:47 +00:00
*/
#include "includes.h"
2006-09-30 13:38:06 +00:00
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
2006-09-30 13:38:06 +00:00
#include <sys/socket.h>
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#include "openbsd-compat/sys-tree.h"
2008-07-23 09:33:08 +00:00
#include "openbsd-compat/sys-queue.h"
2006-09-30 13:38:06 +00:00
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
#include <grp.h>
2022-02-23 18:16:45 +00:00
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
2006-09-30 13:38:06 +00:00
#include <pwd.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
2015-07-02 13:15:34 +00:00
#include <limits.h>
2000-02-24 14:29:47 +00:00
2015-01-05 16:09:55 +00:00
#ifdef WITH_OPENSSL
2001-05-04 04:14:23 +00:00
#include <openssl/dh.h>
#include <openssl/bn.h>
#include <openssl/rand.h>
#include "openbsd-compat/openssl-compat.h"
2015-01-05 16:09:55 +00:00
#endif
2002-06-27 22:42:11 +00:00
#ifdef HAVE_SECUREWARE
#include <sys/security.h>
#include <prot.h>
#endif
2001-05-04 04:14:23 +00:00
#ifdef __FreeBSD__
#include <resolv.h>
#if defined(GSSAPI) && defined(HAVE_GSSAPI_GSSAPI_H)
#include <gssapi/gssapi.h>
#elif defined(GSSAPI) && defined(HAVE_GSSAPI_H)
#include <gssapi.h>
#endif
#endif
2006-09-30 13:38:06 +00:00
#include "xmalloc.h"
2001-05-04 04:14:23 +00:00
#include "ssh.h"
#include "ssh2.h"
#include "sshpty.h"
2000-02-24 14:29:47 +00:00
#include "packet.h"
2001-05-04 04:14:23 +00:00
#include "log.h"
2018-08-28 10:47:58 +00:00
#include "sshbuf.h"
2015-01-05 16:09:55 +00:00
#include "misc.h"
2015-08-26 09:25:17 +00:00
#include "match.h"
2000-02-24 14:29:47 +00:00
#include "servconf.h"
#include "uidswap.h"
#include "compat.h"
2001-05-04 04:14:23 +00:00
#include "cipher.h"
2014-03-22 15:23:38 +00:00
#include "digest.h"
2018-08-28 10:47:58 +00:00
#include "sshkey.h"
2006-09-30 13:38:06 +00:00
#include "kex.h"
#include "authfile.h"
2001-05-04 04:14:23 +00:00
#include "pathnames.h"
#include "atomicio.h"
#include "canohost.h"
2006-09-30 13:38:06 +00:00
#include "hostfile.h"
2001-05-04 04:14:23 +00:00
#include "auth.h"
2013-09-18 17:27:38 +00:00
#include "authfd.h"
2004-10-28 16:11:31 +00:00
#include "msg.h"
2001-05-04 04:14:23 +00:00
#include "dispatch.h"
2002-03-18 10:09:43 +00:00
#include "channels.h"
#include "session.h"
#include "monitor.h"
2006-09-30 13:38:06 +00:00
#ifdef GSSAPI
#include "ssh-gss.h"
#endif
#include "monitor_wrap.h"
2011-09-28 08:14:41 +00:00
#include "ssh-sandbox.h"
2018-05-06 12:27:04 +00:00
#include "auth-options.h"
2006-09-30 13:38:06 +00:00
#include "version.h"
2015-07-02 13:15:34 +00:00
#include "ssherr.h"
2021-02-14 21:04:52 +00:00
#include "sk-api.h"
2021-04-23 19:10:38 +00:00
#include "srclimit.h"
2021-04-23 19:13:32 +00:00
#include "dh.h"
#include "blacklist_client.h"
2000-02-24 14:29:47 +00:00
#ifdef LIBWRAP
#include <tcpd.h>
#include <syslog.h>
#endif /* LIBWRAP */
2004-10-28 16:11:31 +00:00
/* Re-exec fds */
#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1)
#define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2)
#define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3)
#define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4)
2001-05-04 04:14:23 +00:00
extern char *__progname;
2000-02-24 14:29:47 +00:00
/* Server configuration options. */
ServerOptions options;
/* Name of the server configuration file. */
2001-05-04 04:14:23 +00:00
char *config_file_name = _PATH_SERVER_CONFIG_FILE;
2000-02-24 14:29:47 +00:00
/*
* Debug mode flag. This can be set on the command line. If debug
* mode is enabled, extra debugging output will be sent to the system
* log, the daemon will not go to background, and will exit after processing
* the first connection.
*/
int debug_flag = 0;
2018-05-06 12:27:04 +00:00
/*
* 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.
*/
2020-02-14 19:47:15 +00:00
static int test_flag = 0;
2002-03-18 10:09:43 +00:00
2000-02-24 14:29:47 +00:00
/* Flag indicating that the daemon is being started from inetd. */
2020-02-14 19:47:15 +00:00
static int inetd_flag = 0;
2000-02-24 14:29:47 +00:00
2001-05-04 04:14:23 +00:00
/* Flag indicating that sshd should not detach and become a daemon. */
2020-02-14 19:47:15 +00:00
static int no_daemon_flag = 0;
2001-05-04 04:14:23 +00:00
2000-02-24 14:29:47 +00:00
/* debug goes to stderr unless inetd_flag is set */
2020-02-14 19:47:15 +00:00
static int log_stderr = 0;
2000-02-24 14:29:47 +00:00
/* Saved arguments to main(). */
2020-02-14 19:47:15 +00:00
static char **saved_argv;
static int saved_argc;
2000-02-24 14:29:47 +00:00
2004-10-28 16:11:31 +00:00
/* re-exec */
2020-02-14 19:47:15 +00:00
static int rexeced_flag = 0;
static int rexec_flag = 1;
static int rexec_argc = 0;
static char **rexec_argv;
2004-10-28 16:11:31 +00:00
2000-02-24 14:29:47 +00:00
/*
* The sockets that the server is listening; this is used in the SIGHUP
* signal handler.
*/
#define MAX_LISTEN_SOCKS 16
2020-02-14 19:47:15 +00:00
static int listen_socks[MAX_LISTEN_SOCKS];
static int num_listen_socks = 0;
2000-02-24 14:29:47 +00:00
2013-09-18 17:27:38 +00:00
/* Daemon's agent connection */
2015-07-02 13:15:34 +00:00
int auth_sock = -1;
2020-02-14 19:47:15 +00:00
static int have_agent = 0;
2013-09-18 17:27:38 +00:00
2000-02-24 14:29:47 +00:00
/*
* Any really sensitive data in the application is contained in this
* structure. The idea is that this structure could be locked into memory so
* that the pages do not get written into swap. However, there are some
* problems. The private key contains BIGNUMs, and we do not (in principle)
* have access to the internals of them, and locking just the structure is
* not very useful. Currently, memory locking is not implemented.
*/
struct {
2018-05-06 12:24:45 +00:00
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;
2000-02-24 14:29:47 +00:00
} sensitive_data;
2002-03-18 10:09:43 +00:00
/* 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;
2000-02-24 14:29:47 +00:00
/* record remote hostname or ip */
2015-07-02 13:15:34 +00:00
u_int utmp_len = HOST_NAME_MAX+1;
2020-02-14 19:47:15 +00:00
/*
* 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 */
2002-03-18 10:09:43 +00:00
/* variables used for privilege separation */
2006-09-30 13:38:06 +00:00
int use_privsep = -1;
2004-02-26 10:52:33 +00:00
struct monitor *pmonitor = NULL;
2012-08-29 15:46:01 +00:00
int privsep_is_preauth = 1;
2018-05-06 12:24:45 +00:00
static int privsep_chroot = 1;
2020-02-14 19:47:15 +00:00
/* global connection state and authentication contexts */
2004-02-26 10:52:33 +00:00
Authctxt *the_authctxt = NULL;
2020-02-14 19:47:15 +00:00
struct ssh *the_active_state;
2004-02-26 10:52:33 +00:00
2018-05-06 12:27:04 +00:00
/* global key/cert auth options. XXX move to permanent ssh->authctxt? */
struct sshauthopt *auth_opts = NULL;
2006-09-30 13:38:06 +00:00
/* sshd_config buffer */
2018-08-28 10:47:58 +00:00
struct sshbuf *cfg;
2006-09-30 13:38:06 +00:00
2021-02-14 21:04:52 +00:00
/* Included files from the configuration file */
struct include_list includes = TAILQ_HEAD_INITIALIZER(includes);
2004-10-28 16:11:31 +00:00
/* message to be displayed after login */
2018-08-28 10:47:58 +00:00
struct sshbuf *loginmsg;
2004-10-28 16:11:31 +00:00
2006-09-30 13:38:06 +00:00
/* Unprivileged user */
struct passwd *privsep_pw = NULL;
2000-02-24 14:29:47 +00:00
/* Prototypes for various functions defined later in this file. */
2002-03-18 10:09:43 +00:00
void destroy_sensitive_data(void);
void demote_sensitive_data(void);
2020-02-14 19:47:15 +00:00
static void do_ssh2_kex(struct ssh *);
2021-02-14 21:04:52 +00:00
static char *listener_proctitle;
2000-02-24 14:29:47 +00:00
/*
* Close all listening sockets
*/
2002-03-18 10:09:43 +00:00
static void
2000-02-24 14:29:47 +00:00
close_listen_socks(void)
{
int i;
2000-02-24 14:29:47 +00:00
for (i = 0; i < num_listen_socks; i++)
close(listen_socks[i]);
2022-02-23 18:16:45 +00:00
num_listen_socks = 0;
2000-02-24 14:29:47 +00:00
}
2002-03-18 10:09:43 +00:00
static void
close_startup_pipes(void)
{
int i;
2002-03-18 10:09:43 +00:00
if (startup_pipes)
for (i = 0; i < options.max_startups; i++)
if (startup_pipes[i] != -1)
close(startup_pipes[i]);
}
2000-02-24 14:29:47 +00:00
/*
* Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP;
* the effect is to reread the configuration file (and to regenerate
* the server key).
*/
2006-09-30 13:38:06 +00:00
2002-03-18 10:09:43 +00:00
static void
2000-02-24 14:29:47 +00:00
sighup_handler(int sig)
{
received_sighup = 1;
}
/*
* Called from the main program after receiving SIGHUP.
* Restarts the server.
*/
2002-03-18 10:09:43 +00:00
static void
2001-05-04 04:14:23 +00:00
sighup_restart(void)
2000-02-24 14:29:47 +00:00
{
logit("Received SIGHUP; restarting.");
2017-01-31 12:33:47 +00:00
if (options.pid_file != NULL)
unlink(options.pid_file);
2014-01-30 10:56:49 +00:00
platform_pre_restart();
2000-02-24 14:29:47 +00:00
close_listen_socks();
2002-03-18 10:09:43 +00:00
close_startup_pipes();
2021-02-14 21:04:52 +00:00
ssh_signal(SIGHUP, SIG_IGN); /* will be restored after exec */
2000-02-24 14:29:47 +00:00
execv(saved_argv[0], saved_argv);
logit("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0],
strerror(errno));
2000-02-24 14:29:47 +00:00
exit(1);
}
/*
* Generic signal handler for terminating signals in the master daemon.
*/
2002-03-18 10:09:43 +00:00
static void
2000-02-24 14:29:47 +00:00
sigterm_handler(int sig)
{
2002-03-18 10:09:43 +00:00
received_sigterm = sig;
2000-02-24 14:29:47 +00:00
}
/*
* SIGCHLD handler. This is called whenever a child dies. This will then
2002-03-18 10:09:43 +00:00
* reap any zombies left by exited children.
2000-02-24 14:29:47 +00:00
*/
2002-03-18 10:09:43 +00:00
static void
2000-02-24 14:29:47 +00:00
main_sigchld_handler(int sig)
{
int save_errno = errno;
pid_t pid;
2000-02-24 14:29:47 +00:00
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0 ||
2021-02-14 21:00:25 +00:00
(pid == -1 && errno == EINTR))
2000-02-24 14:29:47 +00:00
;
errno = save_errno;
}
/*
* Signal handler for the alarm after the login grace period has expired.
*/
2002-03-18 10:09:43 +00:00
static void
2000-02-24 14:29:47 +00:00
grace_alarm_handler(int sig)
{
2013-03-22 11:19:48 +00:00
/*
* Try to kill any processes that we have spawned, E.g. authorized
2022-02-23 18:16:45 +00:00
* keys command helpers or privsep children.
2013-03-22 11:19:48 +00:00
*/
if (getpgid(0) == getpid()) {
2021-02-14 21:04:52 +00:00
ssh_signal(SIGTERM, SIG_IGN);
2014-01-30 10:56:49 +00:00
kill(0, SIGTERM);
2013-03-22 11:19:48 +00:00
}
BLACKLIST_NOTIFY(the_active_state, BLACKLIST_AUTH_FAIL, "ssh");
2000-02-24 14:29:47 +00:00
/* Log error and exit. */
2022-02-23 18:16:45 +00:00
sigdie("Timeout before authentication for %s port %d",
ssh_remote_ipaddr(the_active_state),
ssh_remote_port(the_active_state));
}
2001-05-04 04:14:23 +00:00
/* Destroy the host and server keys. They will no longer be needed. */
void
destroy_sensitive_data(void)
{
2018-05-06 12:27:04 +00:00
u_int i;
2001-05-04 04:14:23 +00:00
2002-03-18 10:09:43 +00:00
for (i = 0; i < options.num_host_key_files; i++) {
2001-05-04 04:14:23 +00:00
if (sensitive_data.host_keys[i]) {
2018-08-28 10:47:58 +00:00
sshkey_free(sensitive_data.host_keys[i]);
2001-05-04 04:14:23 +00:00
sensitive_data.host_keys[i] = NULL;
}
2010-03-08 11:19:52 +00:00
if (sensitive_data.host_certificates[i]) {
2018-08-28 10:47:58 +00:00
sshkey_free(sensitive_data.host_certificates[i]);
2010-03-08 11:19:52 +00:00
sensitive_data.host_certificates[i] = NULL;
}
2001-05-04 04:14:23 +00:00
}
}
/* Demote private to public keys for network child */
void
demote_sensitive_data(void)
{
2018-05-06 12:24:45 +00:00
struct sshkey *tmp;
2018-05-06 12:27:04 +00:00
u_int i;
2018-08-28 10:47:58 +00:00
int r;
for (i = 0; i < options.num_host_key_files; i++) {
if (sensitive_data.host_keys[i]) {
if ((r = sshkey_from_private(
sensitive_data.host_keys[i], &tmp)) != 0)
2021-04-23 19:10:38 +00:00
fatal_r(r, "could not demote host %s key",
sshkey_type(sensitive_data.host_keys[i]));
2018-08-28 10:47:58 +00:00
sshkey_free(sensitive_data.host_keys[i]);
sensitive_data.host_keys[i] = tmp;
}
2010-03-08 11:19:52 +00:00
/* Certs do not need demotion */
}
2017-01-31 12:33:47 +00:00
}
static void
reseed_prngs(void)
{
u_int32_t rnd[256];
2017-01-31 12:33:47 +00:00
#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)
{
2002-10-29 10:16:02 +00:00
gid_t gidset[1];
/* Enable challenge-response authentication for privilege separation */
privsep_challenge_enable();
2014-03-22 15:23:38 +00:00
#ifdef GSSAPI
/* Cache supported mechanism OIDs for later use */
2019-02-05 15:03:53 +00:00
ssh_gssapi_prepare_supported_oids();
2014-03-22 15:23:38 +00:00
#endif
2017-01-31 12:33:47 +00:00
reseed_prngs();
/* Demote the private keys to public keys. */
demote_sensitive_data();
2016-03-10 20:10:25 +00:00
/* Demote the child */
2018-05-06 12:24:45 +00:00
if (privsep_chroot) {
2016-03-10 20:10:25 +00:00
/* 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;
2021-02-14 21:00:25 +00:00
if (setgroups(1, gidset) == -1)
2016-03-10 20:10:25 +00:00
fatal("setgroups: %.100s", strerror(errno));
permanently_set_uid(privsep_pw);
}
}
2004-02-26 10:52:33 +00:00
static int
2020-02-14 19:47:15 +00:00
privsep_preauth(struct ssh *ssh)
{
2015-07-02 13:15:34 +00:00
int status, r;
pid_t pid;
2011-09-28 08:14:41 +00:00
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 */
2020-02-14 19:47:15 +00:00
pmonitor->m_pkex = &ssh->kex;
2012-08-29 15:55:54 +00:00
if (use_privsep == PRIVSEP_ON)
2014-01-30 10:56:49 +00:00
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);
2012-08-29 15:55:54 +00:00
pmonitor->m_pid = pid;
2015-07-02 13:15:34 +00:00
if (have_agent) {
r = ssh_get_authentication_socket(&auth_sock);
if (r != 0) {
2021-04-23 19:10:38 +00:00
error_r(r, "Could not get agent socket");
2015-07-02 13:15:34 +00:00
have_agent = 0;
}
}
2011-09-28 08:14:41 +00:00
if (box != NULL)
ssh_sandbox_parent_preauth(box, pid);
2020-02-14 19:47:15 +00:00
monitor_child_preauth(ssh, pmonitor);
/* Wait for the child's exit status */
2021-02-14 21:00:25 +00:00
while (waitpid(pid, &status, 0) == -1) {
2012-08-29 15:46:01 +00:00
if (errno == EINTR)
continue;
pmonitor->m_pid = -1;
2021-04-23 19:10:38 +00:00
fatal_f("waitpid: %s", strerror(errno));
2011-09-28 08:14:41 +00:00
}
2012-08-29 15:46:01 +00:00
privsep_is_preauth = 0;
pmonitor->m_pid = -1;
2011-09-28 08:14:41 +00:00
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) != 0)
2021-04-23 19:10:38 +00:00
fatal_f("preauth child exited with status %d",
WEXITSTATUS(status));
2011-09-28 08:14:41 +00:00
} else if (WIFSIGNALED(status))
2021-04-23 19:10:38 +00:00
fatal_f("preauth child terminated by signal %d",
WTERMSIG(status));
2011-09-28 08:14:41 +00:00
if (box != NULL)
ssh_sandbox_parent_finish(box);
return 1;
} else {
/* child */
close(pmonitor->m_sendfd);
2011-09-28 08:14:41 +00:00
close(pmonitor->m_log_recvfd);
/* Arrange for logging to be sent to the monitor */
set_log_handler(mm_log_handler, pmonitor);
2016-03-10 20:10:25 +00:00
privsep_preauth_child();
setproctitle("%s", "[net]");
2011-09-28 08:14:41 +00:00
if (box != NULL)
ssh_sandbox_child(box);
return 0;
}
}
static void
2020-02-14 19:47:15 +00:00
privsep_postauth(struct ssh *ssh, Authctxt *authctxt)
{
2002-10-29 10:16:02 +00:00
#ifdef DISABLE_FD_PASSING
if (1) {
#else
2017-01-31 12:33:47 +00:00
if (authctxt->pw->pw_uid == 0) {
#endif
/* File descriptor passing is broken or root login */
use_privsep = 0;
2006-03-22 20:41:37 +00:00
goto skip;
}
/* New socket pair */
monitor_reinit(pmonitor);
pmonitor->m_pid = fork();
if (pmonitor->m_pid == -1)
fatal("fork of unprivileged child failed");
else if (pmonitor->m_pid != 0) {
2008-07-23 09:33:08 +00:00
verbose("User child is on pid %ld", (long)pmonitor->m_pid);
2018-08-28 10:47:58 +00:00
sshbuf_reset(loginmsg);
2020-02-14 19:47:15 +00:00
monitor_clear_keystate(ssh, pmonitor);
monitor_child_postauth(ssh, pmonitor);
/* NEVERREACHED */
exit(0);
}
2011-09-28 08:14:41 +00:00
/* child */
close(pmonitor->m_sendfd);
2011-09-28 08:14:41 +00:00
pmonitor->m_sendfd = -1;
/* Demote the private keys to public keys. */
demote_sensitive_data();
2017-01-31 12:33:47 +00:00
reseed_prngs();
/* Drop privileges */
do_setusercontext(authctxt->pw);
2006-03-22 20:41:37 +00:00
skip:
/* It is safe now to apply the key state */
2020-02-14 19:47:15 +00:00
monitor_apply_keystate(ssh, pmonitor);
2005-09-03 07:04:25 +00:00
/*
* Tell the packet layer that authentication was successful, since
* this information is not part of the key state.
*/
2020-02-14 19:47:15 +00:00
ssh_packet_set_authenticated(ssh);
}
2018-08-28 10:47:58 +00:00
static void
append_hostkey_type(struct sshbuf *b, const char *s)
{
int r;
if (match_pattern_list(s, options.hostkeyalgorithms, 0) != 1) {
2021-04-23 19:10:38 +00:00
debug3_f("%s key not permitted by HostkeyAlgorithms", s);
2018-08-28 10:47:58 +00:00
return;
}
if ((r = sshbuf_putf(b, "%s%s", sshbuf_len(b) > 0 ? "," : "", s)) != 0)
2021-04-23 19:10:38 +00:00
fatal_fr(r, "sshbuf_putf");
2018-08-28 10:47:58 +00:00
}
2002-03-18 10:09:43 +00:00
static char *
2001-05-04 04:14:23 +00:00
list_hostkey_types(void)
{
2018-08-28 10:47:58 +00:00
struct sshbuf *b;
struct sshkey *key;
2004-02-26 10:52:33 +00:00
char *ret;
2018-05-06 12:27:04 +00:00
u_int i;
2002-03-18 10:09:43 +00:00
2018-08-28 10:47:58 +00:00
if ((b = sshbuf_new()) == NULL)
2021-04-23 19:10:38 +00:00
fatal_f("sshbuf_new failed");
2002-03-18 10:09:43 +00:00
for (i = 0; i < options.num_host_key_files; i++) {
2010-03-08 11:19:52 +00:00
key = sensitive_data.host_keys[i];
2013-09-18 17:27:38 +00:00
if (key == NULL)
key = sensitive_data.host_pubkeys[i];
2017-01-31 12:33:47 +00:00
if (key == NULL)
2015-08-26 09:25:17 +00:00
continue;
2002-03-18 10:09:43 +00:00
switch (key->type) {
2001-05-04 04:14:23 +00:00
case KEY_RSA:
2018-08-28 10:47:58 +00:00
/* for RSA we also support SHA2 signatures */
append_hostkey_type(b, "rsa-sha2-512");
append_hostkey_type(b, "rsa-sha2-256");
/* FALLTHROUGH */
2001-05-04 04:14:23 +00:00
case KEY_DSA:
2011-02-17 11:47:40 +00:00
case KEY_ECDSA:
2014-01-30 10:56:49 +00:00
case KEY_ED25519:
2021-02-14 21:04:52 +00:00
case KEY_ECDSA_SK:
case KEY_ED25519_SK:
2018-05-06 12:27:04 +00:00
case KEY_XMSS:
2018-08-28 10:47:58 +00:00
append_hostkey_type(b, sshkey_ssh_name(key));
2001-05-04 04:14:23 +00:00
break;
}
2010-03-08 11:19:52 +00:00
/* If the private key has a cert peer, then list that too */
key = sensitive_data.host_certificates[i];
if (key == NULL)
continue;
switch (key->type) {
case KEY_RSA_CERT:
2018-08-28 10:47:58 +00:00
/* 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 */
2010-03-08 11:19:52 +00:00
case KEY_DSA_CERT:
2011-02-17 11:47:40 +00:00
case KEY_ECDSA_CERT:
2014-01-30 10:56:49 +00:00
case KEY_ED25519_CERT:
2021-02-14 21:04:52 +00:00
case KEY_ECDSA_SK_CERT:
case KEY_ED25519_SK_CERT:
2018-05-06 12:27:04 +00:00
case KEY_XMSS_CERT:
2018-08-28 10:47:58 +00:00
append_hostkey_type(b, sshkey_ssh_name(key));
2010-03-08 11:19:52 +00:00
break;
}
2001-05-04 04:14:23 +00:00
}
2018-08-28 10:47:58 +00:00
if ((ret = sshbuf_dup_string(b)) == NULL)
2021-04-23 19:10:38 +00:00
fatal_f("sshbuf_dup_string failed");
2018-08-28 10:47:58 +00:00
sshbuf_free(b);
2021-04-23 19:10:38 +00:00
debug_f("%s", ret);
2004-02-26 10:52:33 +00:00
return ret;
2001-05-04 04:14:23 +00:00
}
2018-05-06 12:24:45 +00:00
static struct sshkey *
2015-07-02 13:15:34 +00:00
get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
2001-05-04 04:14:23 +00:00
{
2018-05-06 12:27:04 +00:00
u_int i;
2018-05-06 12:24:45 +00:00
struct sshkey *key;
2002-03-18 10:09:43 +00:00
for (i = 0; i < options.num_host_key_files; i++) {
2010-11-08 10:45:44 +00:00
switch (type) {
case KEY_RSA_CERT:
case KEY_DSA_CERT:
2011-02-17 11:47:40 +00:00
case KEY_ECDSA_CERT:
2014-01-30 10:56:49 +00:00
case KEY_ED25519_CERT:
2021-02-14 21:04:52 +00:00
case KEY_ECDSA_SK_CERT:
case KEY_ED25519_SK_CERT:
2018-05-06 12:27:04 +00:00
case KEY_XMSS_CERT:
2010-03-08 11:19:52 +00:00
key = sensitive_data.host_certificates[i];
2010-11-08 10:45:44 +00:00
break;
default:
2010-03-08 11:19:52 +00:00
key = sensitive_data.host_keys[i];
2013-09-18 17:27:38 +00:00
if (key == NULL && !need_private)
key = sensitive_data.host_pubkeys[i];
2010-11-08 10:45:44 +00:00
break;
}
2021-02-14 21:04:52 +00:00
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:
2010-03-08 11:19:52 +00:00
return need_private ?
sensitive_data.host_keys[i] : key;
2021-02-14 21:04:52 +00:00
}
2001-05-04 04:14:23 +00:00
}
return NULL;
}
2018-05-06 12:24:45 +00:00
struct sshkey *
2015-07-02 13:15:34 +00:00
get_hostkey_public_by_type(int type, int nid, struct ssh *ssh)
2010-03-08 11:19:52 +00:00
{
2015-07-02 13:15:34 +00:00
return get_hostkey_by_type(type, nid, 0, ssh);
2010-03-08 11:19:52 +00:00
}
2018-05-06 12:24:45 +00:00
struct sshkey *
2015-07-02 13:15:34 +00:00
get_hostkey_private_by_type(int type, int nid, struct ssh *ssh)
2010-03-08 11:19:52 +00:00
{
2015-07-02 13:15:34 +00:00
return get_hostkey_by_type(type, nid, 1, ssh);
2010-03-08 11:19:52 +00:00
}
2018-05-06 12:24:45 +00:00
struct sshkey *
get_hostkey_by_index(int ind)
{
2018-05-06 12:27:04 +00:00
if (ind < 0 || (u_int)ind >= options.num_host_key_files)
return (NULL);
return (sensitive_data.host_keys[ind]);
}
2018-05-06 12:24:45 +00:00
struct sshkey *
2015-07-02 13:15:34 +00:00
get_hostkey_public_by_index(int ind, struct ssh *ssh)
2013-09-18 17:27:38 +00:00
{
2018-05-06 12:27:04 +00:00
if (ind < 0 || (u_int)ind >= options.num_host_key_files)
2013-09-18 17:27:38 +00:00
return (NULL);
return (sensitive_data.host_pubkeys[ind]);
}
int
2018-05-06 12:24:45 +00:00
get_hostkey_index(struct sshkey *key, int compare, struct ssh *ssh)
{
2018-05-06 12:27:04 +00:00
u_int i;
for (i = 0; i < options.num_host_key_files; i++) {
2018-08-28 10:47:58 +00:00
if (sshkey_is_cert(key)) {
2015-07-02 13:15:34 +00:00
if (key == sensitive_data.host_certificates[i] ||
(compare && sensitive_data.host_certificates[i] &&
sshkey_equal(key,
sensitive_data.host_certificates[i])))
2010-03-08 11:19:52 +00:00
return (i);
} else {
2015-07-02 13:15:34 +00:00
if (key == sensitive_data.host_keys[i] ||
(compare && sensitive_data.host_keys[i] &&
sshkey_equal(key, sensitive_data.host_keys[i])))
2010-03-08 11:19:52 +00:00
return (i);
2015-07-02 13:15:34 +00:00
if (key == sensitive_data.host_pubkeys[i] ||
(compare && sensitive_data.host_pubkeys[i] &&
sshkey_equal(key, sensitive_data.host_pubkeys[i])))
2013-09-18 17:27:38 +00:00
return (i);
2010-03-08 11:19:52 +00:00
}
}
return (-1);
}
2015-07-02 13:15:34 +00:00
/* Inform the client of all hostkeys */
static void
notify_hostkeys(struct ssh *ssh)
{
struct sshbuf *buf;
struct sshkey *key;
2018-05-06 12:27:04 +00:00
u_int i, nkeys;
int r;
2015-07-02 13:15:34 +00:00
char *fp;
2015-07-02 13:18:50 +00:00
/* Some clients cannot cope with the hostkeys message, skip those. */
2020-02-14 19:47:15 +00:00
if (ssh->compat & SSH_BUG_HOSTKEYS)
2015-07-02 13:18:50 +00:00
return;
2015-07-02 13:15:34 +00:00
if ((buf = sshbuf_new()) == NULL)
2021-04-23 19:10:38 +00:00
fatal_f("sshbuf_new");
2015-07-02 13:15:34 +00:00
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 ||
2017-01-31 12:33:47 +00:00
sshkey_is_cert(key))
2015-07-02 13:15:34 +00:00
continue;
fp = sshkey_fingerprint(key, options.fingerprint_hash,
SSH_FP_DEFAULT);
2021-04-23 19:10:38 +00:00
debug3_f("key %d: %s %s", i, sshkey_ssh_name(key), fp);
2015-07-02 13:15:34 +00:00
free(fp);
if (nkeys == 0) {
2020-02-14 19:47:15 +00:00
/*
* 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__);
2015-07-02 13:15:34 +00:00
}
2020-02-14 19:47:15 +00:00
/* Append the key to the request */
2015-07-02 13:15:34 +00:00
sshbuf_reset(buf);
if ((r = sshkey_putb(key, buf)) != 0)
2021-04-23 19:10:38 +00:00
fatal_fr(r, "couldn't put hostkey %d", i);
2020-02-14 19:47:15 +00:00
if ((r = sshpkt_put_stringb(ssh, buf)) != 0)
sshpkt_fatal(ssh, r, "%s: append key", __func__);
2015-07-02 13:15:34 +00:00
nkeys++;
}
2021-04-23 19:10:38 +00:00
debug3_f("sent %u hostkeys", nkeys);
2015-07-02 13:15:34 +00:00
if (nkeys == 0)
2021-04-23 19:10:38 +00:00
fatal_f("no hostkeys");
2020-02-14 19:47:15 +00:00
if ((r = sshpkt_send(ssh)) != 0)
sshpkt_fatal(ssh, r, "%s: send", __func__);
2015-07-02 13:15:34 +00:00
sshbuf_free(buf);
}
/*
* returns 1 if connection should be dropped, 0 otherwise.
* dropping starts at connection #max_startups_begin with a probability
* of (max_startups_rate/100). the probability increases linearly until
* all connections are dropped for startups > max_startups
*/
2002-03-18 10:09:43 +00:00
static int
2021-02-14 21:09:58 +00:00
should_drop_connection(int startups)
{
2005-06-05 15:46:09 +00:00
int p, r;
if (startups < options.max_startups_begin)
return 0;
if (startups >= options.max_startups)
return 1;
if (options.max_startups_rate == 100)
return 1;
p = 100 - options.max_startups_rate;
p *= startups - options.max_startups_begin;
2005-06-05 15:46:09 +00:00
p /= options.max_startups - options.max_startups_begin;
p += options.max_startups_rate;
2008-07-23 09:33:08 +00:00
r = arc4random_uniform(100);
2021-04-23 19:10:38 +00:00
debug_f("p %d, r %d", p, r);
return (r < p) ? 1 : 0;
}
2021-02-14 21:09:58 +00:00
/*
* Check whether connection should be accepted by MaxStartups.
* Returns 0 if the connection is accepted. If the connection is refused,
* returns 1 and attempts to send notification to client.
* Logs when the MaxStartups condition is entered or exited, and periodically
* while in that state.
*/
static int
2021-04-23 19:10:38 +00:00
drop_connection(int sock, int startups, int notify_pipe)
2021-02-14 21:09:58 +00:00
{
char *laddr, *raddr;
const char msg[] = "Exceeded MaxStartups\r\n";
static time_t last_drop, first_drop;
static u_int ndropped;
LogLevel drop_level = SYSLOG_LEVEL_VERBOSE;
time_t now;
now = monotime();
2021-04-23 19:10:38 +00:00
if (!should_drop_connection(startups) &&
srclimit_check_allow(sock, notify_pipe) == 1) {
2021-02-14 21:09:58 +00:00
if (last_drop != 0 &&
startups < options.max_startups_begin - 1) {
/* XXX maybe need better hysteresis here */
logit("exited MaxStartups throttling after %s, "
"%u connections dropped",
fmt_timeframe(now - first_drop), ndropped);
last_drop = 0;
}
return 0;
}
#define SSHD_MAXSTARTUPS_LOG_INTERVAL (5 * 60)
if (last_drop == 0) {
error("beginning MaxStartups throttling");
drop_level = SYSLOG_LEVEL_INFO;
first_drop = now;
ndropped = 0;
} else if (last_drop + SSHD_MAXSTARTUPS_LOG_INTERVAL < now) {
/* Periodic logs */
error("in MaxStartups throttling for %s, "
"%u connections dropped",
fmt_timeframe(now - first_drop), ndropped + 1);
drop_level = SYSLOG_LEVEL_INFO;
}
last_drop = now;
ndropped++;
laddr = get_local_ipaddr(sock);
raddr = get_peer_ipaddr(sock);
do_log2(drop_level, "drop connection #%d from [%s]:%d on [%s]:%d "
"past MaxStartups", startups, raddr, get_peer_port(sock),
laddr, get_local_port(sock));
free(laddr);
free(raddr);
/* best-effort notification to client */
(void)write(sock, msg, sizeof(msg) - 1);
return 1;
}
2002-03-18 10:09:43 +00:00
static void
usage(void)
{
if (options.version_addendum != NULL &&
*options.version_addendum != '\0')
fprintf(stderr, "%s %s, %s\n",
SSH_RELEASE,
options.version_addendum, SSH_OPENSSL_VERSION);
2012-09-03 16:51:41 +00:00
else
fprintf(stderr, "%s, %s\n",
SSH_RELEASE, SSH_OPENSSL_VERSION);
2004-04-20 09:46:41 +00:00
fprintf(stderr,
2023-03-16 12:41:22 +00:00
"usage: sshd [-46DdeGiqTtV] [-C connection_spec] [-c host_cert_file]\n"
2013-09-18 17:27:38 +00:00
" [-E log_file] [-f config_file] [-g login_grace_time]\n"
2017-01-31 12:33:47 +00:00
" [-h host_key_file] [-o option] [-p port] [-u len]\n"
2004-04-20 09:46:41 +00:00
);
2002-03-18 10:09:43 +00:00
exit(1);
}
2004-10-28 16:11:31 +00:00
static void
2017-01-31 12:29:48 +00:00
send_rexec_state(int fd, struct sshbuf *conf)
2004-10-28 16:11:31 +00:00
{
2021-02-14 21:04:52 +00:00
struct sshbuf *m = NULL, *inc = NULL;
struct include_item *item = NULL;
2017-01-31 12:29:48 +00:00
int r;
2004-10-28 16:11:31 +00:00
2021-04-23 19:10:38 +00:00
debug3_f("entering fd = %d config len %zu", fd,
2017-01-31 12:29:48 +00:00
sshbuf_len(conf));
2004-10-28 16:11:31 +00:00
2021-02-14 21:04:52 +00:00
if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL)
2021-04-23 19:10:38 +00:00
fatal_f("sshbuf_new failed");
2021-02-14 21:04:52 +00:00
/* 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)
2021-04-23 19:10:38 +00:00
fatal_fr(r, "compose includes");
2021-02-14 21:04:52 +00:00
}
2004-10-28 16:11:31 +00:00
/*
* Protocol from reexec master to child:
* string configuration
2021-02-14 21:04:52 +00:00
* string included_files[] {
* string selector
* string filename
* string contents
* }
2004-10-28 16:11:31 +00:00
*/
2021-02-14 21:04:52 +00:00
if ((r = sshbuf_put_stringb(m, conf)) != 0 ||
(r = sshbuf_put_stringb(m, inc)) != 0)
2021-04-23 19:10:38 +00:00
fatal_fr(r, "compose config");
2017-01-31 12:29:48 +00:00
if (ssh_msg_send(fd, 0, m) == -1)
2021-04-23 19:10:38 +00:00
error_f("ssh_msg_send failed");
2004-10-28 16:11:31 +00:00
2017-01-31 12:29:48 +00:00
sshbuf_free(m);
2021-02-14 21:04:52 +00:00
sshbuf_free(inc);
2004-10-28 16:11:31 +00:00
2021-04-23 19:10:38 +00:00
debug3_f("done");
2004-10-28 16:11:31 +00:00
}
static void
2018-08-28 10:47:58 +00:00
recv_rexec_state(int fd, struct sshbuf *conf)
2004-10-28 16:11:31 +00:00
{
2021-02-14 21:04:52 +00:00
struct sshbuf *m, *inc;
2018-08-28 10:47:58 +00:00
u_char *cp, ver;
size_t len;
int r;
2021-02-14 21:04:52 +00:00
struct include_item *item;
2004-10-28 16:11:31 +00:00
2021-04-23 19:10:38 +00:00
debug3_f("entering fd = %d", fd);
2004-10-28 16:11:31 +00:00
2021-02-14 21:04:52 +00:00
if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL)
2021-04-23 19:10:38 +00:00
fatal_f("sshbuf_new failed");
2018-08-28 10:47:58 +00:00
if (ssh_msg_recv(fd, m) == -1)
2021-04-23 19:10:38 +00:00
fatal_f("ssh_msg_recv failed");
2018-08-28 10:47:58 +00:00
if ((r = sshbuf_get_u8(m, &ver)) != 0)
2021-04-23 19:10:38 +00:00
fatal_fr(r, "parse version");
2018-08-28 10:47:58 +00:00
if (ver != 0)
2021-04-23 19:10:38 +00:00
fatal_f("rexec version mismatch");
2021-02-14 21:04:52 +00:00
if ((r = sshbuf_get_string(m, &cp, &len)) != 0 ||
(r = sshbuf_get_stringb(m, inc)) != 0)
2021-04-23 19:10:38 +00:00
fatal_fr(r, "parse config");
2021-02-14 21:04:52 +00:00
if (conf != NULL && (r = sshbuf_put(conf, cp, len)))
2021-04-23 19:10:38 +00:00
fatal_fr(r, "sshbuf_put");
2021-02-14 21:04:52 +00:00
while (sshbuf_len(inc) != 0) {
item = xcalloc(1, sizeof(*item));
if ((item->contents = sshbuf_new()) == NULL)
2021-04-23 19:10:38 +00:00
fatal_f("sshbuf_new failed");
2021-02-14 21:04:52 +00:00
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)
2021-04-23 19:10:38 +00:00
fatal_fr(r, "parse includes");
2021-02-14 21:04:52 +00:00
TAILQ_INSERT_TAIL(&includes, item, entry);
}
2018-08-28 10:47:58 +00:00
free(cp);
sshbuf_free(m);
2004-10-28 16:11:31 +00:00
2021-04-23 19:10:38 +00:00
debug3_f("done");
2004-10-28 16:11:31 +00:00
}
2006-09-30 13:38:06 +00:00
/* Accept a connection from inetd */
static void
server_accept_inetd(int *sock_in, int *sock_out)
{
if (rexeced_flag) {
close(REEXEC_CONFIG_PASS_FD);
*sock_in = *sock_out = dup(STDIN_FILENO);
} else {
*sock_in = dup(STDIN_FILENO);
*sock_out = dup(STDOUT_FILENO);
}
/*
* We intentionally do not close the descriptors 0, 1, and 2
* as our code for setting the descriptors won't work if
* ttyfd happens to be one of those.
*/
2021-04-23 19:10:38 +00:00
if (stdfd_devnull(1, 1, !log_stderr) == -1)
error_f("stdfd_devnull failed");
2006-09-30 13:38:06 +00:00
debug("inetd sockets after dupping: %d, %d", *sock_in, *sock_out);
}
/*
* Listen for TCP connections
*/
static void
2018-05-06 12:27:04 +00:00
listen_on_addrs(struct listenaddr *la)
2006-09-30 13:38:06 +00:00
{
2018-05-06 12:27:04 +00:00
int ret, listen_sock;
2006-09-30 13:38:06 +00:00
struct addrinfo *ai;
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
2018-05-06 12:27:04 +00:00
for (ai = la->addrs; ai; ai = ai->ai_next) {
2006-09-30 13:38:06 +00:00
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
continue;
if (num_listen_socks >= MAX_LISTEN_SOCKS)
fatal("Too many listen sockets. "
"Enlarge MAX_LISTEN_SOCKS");
if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen,
ntop, sizeof(ntop), strport, sizeof(strport),
NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
error("getnameinfo failed: %.100s",
ssh_gai_strerror(ret));
2006-09-30 13:38:06 +00:00
continue;
}
/* Create socket for listening. */
listen_sock = socket(ai->ai_family, ai->ai_socktype,
ai->ai_protocol);
2021-02-14 21:00:25 +00:00
if (listen_sock == -1) {
2006-09-30 13:38:06 +00:00
/* kernel may not support ipv6 */
verbose("socket: %.100s", strerror(errno));
continue;
}
if (set_nonblock(listen_sock) == -1) {
close(listen_sock);
continue;
}
2017-08-03 10:10:20 +00:00
if (fcntl(listen_sock, F_SETFD, FD_CLOEXEC) == -1) {
verbose("socket: CLOEXEC: %s", strerror(errno));
close(listen_sock);
continue;
}
2018-05-06 12:27:04 +00:00
/* Socket options */
set_reuseaddr(listen_sock);
if (la->rdomain != NULL &&
set_rdomain(listen_sock, la->rdomain) == -1) {
close(listen_sock);
continue;
}
2006-09-30 13:38:06 +00:00
/* Only communicate in IPv6 over AF_INET6 sockets. */
2010-03-08 11:19:52 +00:00
if (ai->ai_family == AF_INET6)
sock_set_v6only(listen_sock);
2006-09-30 13:38:06 +00:00
debug("Bind to port %s on %s.", strport, ntop);
/* Bind the socket to the desired port. */
2021-02-14 21:00:25 +00:00
if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) == -1) {
2006-09-30 13:38:06 +00:00
error("Bind to port %s on %s failed: %.200s.",
strport, ntop, strerror(errno));
close(listen_sock);
continue;
}
listen_socks[num_listen_socks] = listen_sock;
num_listen_socks++;
/* Start listening on the port. */
2021-02-14 21:00:25 +00:00
if (listen(listen_sock, SSH_LISTEN_BACKLOG) == -1)
2006-09-30 13:38:06 +00:00
fatal("listen on [%s]:%s: %.100s",
ntop, strport, strerror(errno));
2018-05-06 12:27:04 +00:00
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;
2021-04-23 19:10:38 +00:00
/* Initialise per-source limit tracking. */
srclimit_init(options.max_startups, options.per_source_max_startups,
options.per_source_masklen_ipv4, options.per_source_masklen_ipv6);
2018-05-06 12:27:04 +00:00
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]));
2006-09-30 13:38:06 +00:00
}
2018-05-06 12:27:04 +00:00
free(options.listen_addrs);
options.listen_addrs = NULL;
options.num_listen_addrs = 0;
2006-09-30 13:38:06 +00:00
if (!num_listen_socks)
fatal("Cannot bind any address.");
}
/*
* The main TCP accept loop. Note that, for the non-debug case, returns
* from this function are in a forked subprocess.
*/
static void
server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
{
2022-02-23 18:16:45 +00:00
struct pollfd *pfd = NULL;
int i, j, ret, npfd;
2021-02-14 21:04:52 +00:00
int ostartups = -1, startups = 0, listening = 0, lameduck = 0;
int startup_p[2] = { -1 , -1 }, *startup_pollfd;
2020-02-14 19:47:15 +00:00
char c = 0;
2006-09-30 13:38:06 +00:00
struct sockaddr_storage from;
socklen_t fromlen;
pid_t pid;
2014-01-30 10:56:49 +00:00
u_char rnd[256];
2021-08-30 19:14:33 +00:00
sigset_t nsigset, osigset;
sshd: update the libwrap patch to drop connections early OpenSSH has dropped libwrap support in OpenSSH 6.7p in 2014 (f2719b7c in github.com/openssh/openssh-portable) and we maintain the patch ourselves since 2016 (a0ee8cc636cd). Over the years, the libwrap support has deteriotated and probably that was reason for removal upstream. Original idea of libwrap was to drop illegitimate connection as soon as possible, but over the years the code was pushed further down and down and ended in the forked client connection handler. The negative effects of late dropping is increasing attack surface for hosts that are to be dropped anyway. Apart from hypothetical future vulnerabilities in connection handling, today a malicious host listed in /etc/hosts.allow still can trigger sshd to enter connection throttling mode, which is enabled by default (see MaxStartups in sshd_config(5)), effectively casting DoS attack. Note that on OpenBSD this attack isn't possible, since they enable MaxStartups together with UseBlacklist. A only negative effect from early drop, that I can imagine, is that now main listener parses file in /etc, and if our root filesystems goes bad, it would get stuck. But unlikely you'd be able to login in that case anyway. Implementation details: - For brevity we reuse the same struct request_info. This isn't a documented feature of libwrap, but code review, viewing data in a debugger and real life testing shows that if we clear RQ_CLIENT_NAME and RQ_CLIENT_ADDR every time, it works as intended. - We set SO_LINGER on the socket to force immediate connection reset. - We log message exactly as libwrap's refuse() would do. Differential revision: https://reviews.freebsd.org/D33044
2022-01-03 02:32:30 +00:00
#ifdef LIBWRAP
struct request_info req;
request_init(&req, RQ_DAEMON, __progname, 0);
#endif
2006-09-30 13:38:06 +00:00
2021-02-14 21:07:21 +00:00
/* pipes connected to unauthenticated child sshd processes */
2006-09-30 13:38:06 +00:00
startup_pipes = xcalloc(options.max_startups, sizeof(int));
2020-02-14 19:47:15 +00:00
startup_flags = xcalloc(options.max_startups, sizeof(int));
startup_pollfd = xcalloc(options.max_startups, sizeof(int));
2006-09-30 13:38:06 +00:00
for (i = 0; i < options.max_startups; i++)
startup_pipes[i] = -1;
2021-08-30 19:14:33 +00:00
/*
* Prepare signal mask that we use to block signals that might set
* received_sigterm or received_sighup, so that we are guaranteed
2022-02-23 18:16:45 +00:00
* to immediately wake up the ppoll if a signal is received after
2021-08-30 19:14:33 +00:00
* the flag is checked.
*/
sigemptyset(&nsigset);
sigaddset(&nsigset, SIGHUP);
sigaddset(&nsigset, SIGCHLD);
sigaddset(&nsigset, SIGTERM);
sigaddset(&nsigset, SIGQUIT);
/* sized for worst-case */
2022-02-23 18:16:45 +00:00
pfd = xcalloc(num_listen_socks + options.max_startups,
sizeof(struct pollfd));
2006-09-30 13:38:06 +00:00
/*
* Stay listening for connections until the system crashes or
* the daemon is killed with a signal.
*/
for (;;) {
2021-08-30 19:14:33 +00:00
sigprocmask(SIG_BLOCK, &nsigset, &osigset);
if (received_sigterm) {
logit("Received signal %d; terminating.",
(int) received_sigterm);
close_listen_socks();
if (options.pid_file != NULL)
unlink(options.pid_file);
exit(received_sigterm == SIGTERM ? 0 : 255);
}
2021-02-14 21:04:52 +00:00
if (ostartups != startups) {
setproctitle("%s [listener] %d of %d-%d startups",
listener_proctitle, startups,
options.max_startups_begin, options.max_startups);
ostartups = startups;
}
2020-02-14 19:47:15 +00:00
if (received_sighup) {
if (!lameduck) {
debug("Received SIGHUP; waiting for children");
close_listen_socks();
lameduck = 1;
}
2021-08-30 19:14:33 +00:00
if (listening <= 0) {
sigprocmask(SIG_SETMASK, &osigset, NULL);
2020-02-14 19:47:15 +00:00
sighup_restart();
2021-08-30 19:14:33 +00:00
}
2020-02-14 19:47:15 +00:00
}
2006-09-30 13:38:06 +00:00
2022-02-23 18:16:45 +00:00
for (i = 0; i < num_listen_socks; i++) {
pfd[i].fd = listen_socks[i];
pfd[i].events = POLLIN;
}
npfd = num_listen_socks;
2022-02-23 18:16:45 +00:00
for (i = 0; i < options.max_startups; i++) {
startup_pollfd[i] = -1;
if (startup_pipes[i] != -1) {
pfd[npfd].fd = startup_pipes[i];
pfd[npfd].events = POLLIN;
startup_pollfd[i] = npfd++;
}
2022-02-23 18:16:45 +00:00
}
2006-09-30 13:38:06 +00:00
2021-08-30 19:14:33 +00:00
/* Wait until a connection arrives or a child exits. */
ret = ppoll(pfd, npfd, NULL, &osigset);
if (ret == -1 && errno != EINTR) {
2022-02-23 18:16:45 +00:00
error("ppoll: %.100s", strerror(errno));
if (errno == EINVAL)
cleanup_exit(1); /* can't recover */
}
2021-08-30 19:14:33 +00:00
sigprocmask(SIG_SETMASK, &osigset, NULL);
2021-02-14 21:00:25 +00:00
if (ret == -1)
2006-09-30 13:38:06 +00:00
continue;
2020-02-14 19:47:15 +00:00
for (i = 0; i < options.max_startups; i++) {
if (startup_pipes[i] == -1 ||
startup_pollfd[i] == -1 ||
!(pfd[startup_pollfd[i]].revents & (POLLIN|POLLHUP)))
2020-02-14 19:47:15 +00:00
continue;
switch (read(startup_pipes[i], &c, sizeof(c))) {
case -1:
if (errno == EINTR || errno == EAGAIN)
continue;
if (errno != EPIPE) {
2021-04-23 19:10:38 +00:00
error_f("startup pipe %d (fd=%d): "
"read %s", i, startup_pipes[i],
strerror(errno));
2020-02-14 19:47:15 +00:00
}
/* FALLTHROUGH */
case 0:
/* child exited or completed auth */
2006-09-30 13:38:06 +00:00
close(startup_pipes[i]);
2021-04-23 19:10:38 +00:00
srclimit_done(startup_pipes[i]);
2006-09-30 13:38:06 +00:00
startup_pipes[i] = -1;
startups--;
2020-02-14 19:47:15 +00:00
if (startup_flags[i])
listening--;
break;
case 1:
/* child has finished preliminaries */
if (startup_flags[i]) {
listening--;
startup_flags[i] = 0;
}
break;
2006-09-30 13:38:06 +00:00
}
2020-02-14 19:47:15 +00:00
}
2006-09-30 13:38:06 +00:00
for (i = 0; i < num_listen_socks; i++) {
2022-02-23 18:16:45 +00:00
if (!(pfd[i].revents & POLLIN))
2006-09-30 13:38:06 +00:00
continue;
fromlen = sizeof(from);
*newsock = accept(listen_socks[i],
(struct sockaddr *)&from, &fromlen);
2021-02-14 21:00:25 +00:00
if (*newsock == -1) {
2013-09-18 17:27:38 +00:00
if (errno != EINTR && errno != EWOULDBLOCK &&
errno != ECONNABORTED && errno != EAGAIN)
2012-08-29 15:55:54 +00:00
error("accept: %.100s",
strerror(errno));
if (errno == EMFILE || errno == ENFILE)
usleep(100 * 1000);
2006-09-30 13:38:06 +00:00
continue;
}
sshd: update the libwrap patch to drop connections early OpenSSH has dropped libwrap support in OpenSSH 6.7p in 2014 (f2719b7c in github.com/openssh/openssh-portable) and we maintain the patch ourselves since 2016 (a0ee8cc636cd). Over the years, the libwrap support has deteriotated and probably that was reason for removal upstream. Original idea of libwrap was to drop illegitimate connection as soon as possible, but over the years the code was pushed further down and down and ended in the forked client connection handler. The negative effects of late dropping is increasing attack surface for hosts that are to be dropped anyway. Apart from hypothetical future vulnerabilities in connection handling, today a malicious host listed in /etc/hosts.allow still can trigger sshd to enter connection throttling mode, which is enabled by default (see MaxStartups in sshd_config(5)), effectively casting DoS attack. Note that on OpenBSD this attack isn't possible, since they enable MaxStartups together with UseBlacklist. A only negative effect from early drop, that I can imagine, is that now main listener parses file in /etc, and if our root filesystems goes bad, it would get stuck. But unlikely you'd be able to login in that case anyway. Implementation details: - For brevity we reuse the same struct request_info. This isn't a documented feature of libwrap, but code review, viewing data in a debugger and real life testing shows that if we clear RQ_CLIENT_NAME and RQ_CLIENT_ADDR every time, it works as intended. - We set SO_LINGER on the socket to force immediate connection reset. - We log message exactly as libwrap's refuse() would do. Differential revision: https://reviews.freebsd.org/D33044
2022-01-03 02:32:30 +00:00
#ifdef LIBWRAP
/* Check whether logins are denied from this host. */
request_set(&req, RQ_FILE, *newsock,
RQ_CLIENT_NAME, "", RQ_CLIENT_ADDR, "", 0);
sock_host(&req);
if (!hosts_access(&req)) {
const struct linger l = { .l_onoff = 1,
.l_linger = 0 };
(void )setsockopt(*newsock, SOL_SOCKET,
SO_LINGER, &l, sizeof(l));
(void )close(*newsock);
/*
* Mimic message from libwrap's refuse() as
* precisely as we can afford. The authentic
* message prints the IP address and the
* hostname it resolves to in parentheses. If
* the IP address cannot be resolved to a
* hostname, the IP address will be repeated
* in parentheses. As name resolution in the
* main server loop could stall, and logging
* resolved names adds little or no value to
* incident investigation, this implementation
* only repeats the IP address in parentheses.
* This should resemble librwap's refuse()
* closely enough not to break auditing
* software like sshguard or custom scripts.
sshd: update the libwrap patch to drop connections early OpenSSH has dropped libwrap support in OpenSSH 6.7p in 2014 (f2719b7c in github.com/openssh/openssh-portable) and we maintain the patch ourselves since 2016 (a0ee8cc636cd). Over the years, the libwrap support has deteriotated and probably that was reason for removal upstream. Original idea of libwrap was to drop illegitimate connection as soon as possible, but over the years the code was pushed further down and down and ended in the forked client connection handler. The negative effects of late dropping is increasing attack surface for hosts that are to be dropped anyway. Apart from hypothetical future vulnerabilities in connection handling, today a malicious host listed in /etc/hosts.allow still can trigger sshd to enter connection throttling mode, which is enabled by default (see MaxStartups in sshd_config(5)), effectively casting DoS attack. Note that on OpenBSD this attack isn't possible, since they enable MaxStartups together with UseBlacklist. A only negative effect from early drop, that I can imagine, is that now main listener parses file in /etc, and if our root filesystems goes bad, it would get stuck. But unlikely you'd be able to login in that case anyway. Implementation details: - For brevity we reuse the same struct request_info. This isn't a documented feature of libwrap, but code review, viewing data in a debugger and real life testing shows that if we clear RQ_CLIENT_NAME and RQ_CLIENT_ADDR every time, it works as intended. - We set SO_LINGER on the socket to force immediate connection reset. - We log message exactly as libwrap's refuse() would do. Differential revision: https://reviews.freebsd.org/D33044
2022-01-03 02:32:30 +00:00
*/
syslog(LOG_WARNING,
sshd: update the libwrap patch to drop connections early OpenSSH has dropped libwrap support in OpenSSH 6.7p in 2014 (f2719b7c in github.com/openssh/openssh-portable) and we maintain the patch ourselves since 2016 (a0ee8cc636cd). Over the years, the libwrap support has deteriotated and probably that was reason for removal upstream. Original idea of libwrap was to drop illegitimate connection as soon as possible, but over the years the code was pushed further down and down and ended in the forked client connection handler. The negative effects of late dropping is increasing attack surface for hosts that are to be dropped anyway. Apart from hypothetical future vulnerabilities in connection handling, today a malicious host listed in /etc/hosts.allow still can trigger sshd to enter connection throttling mode, which is enabled by default (see MaxStartups in sshd_config(5)), effectively casting DoS attack. Note that on OpenBSD this attack isn't possible, since they enable MaxStartups together with UseBlacklist. A only negative effect from early drop, that I can imagine, is that now main listener parses file in /etc, and if our root filesystems goes bad, it would get stuck. But unlikely you'd be able to login in that case anyway. Implementation details: - For brevity we reuse the same struct request_info. This isn't a documented feature of libwrap, but code review, viewing data in a debugger and real life testing shows that if we clear RQ_CLIENT_NAME and RQ_CLIENT_ADDR every time, it works as intended. - We set SO_LINGER on the socket to force immediate connection reset. - We log message exactly as libwrap's refuse() would do. Differential revision: https://reviews.freebsd.org/D33044
2022-01-03 02:32:30 +00:00
"refused connect from %s (%s)",
eval_hostaddr(req.client),
sshd: update the libwrap patch to drop connections early OpenSSH has dropped libwrap support in OpenSSH 6.7p in 2014 (f2719b7c in github.com/openssh/openssh-portable) and we maintain the patch ourselves since 2016 (a0ee8cc636cd). Over the years, the libwrap support has deteriotated and probably that was reason for removal upstream. Original idea of libwrap was to drop illegitimate connection as soon as possible, but over the years the code was pushed further down and down and ended in the forked client connection handler. The negative effects of late dropping is increasing attack surface for hosts that are to be dropped anyway. Apart from hypothetical future vulnerabilities in connection handling, today a malicious host listed in /etc/hosts.allow still can trigger sshd to enter connection throttling mode, which is enabled by default (see MaxStartups in sshd_config(5)), effectively casting DoS attack. Note that on OpenBSD this attack isn't possible, since they enable MaxStartups together with UseBlacklist. A only negative effect from early drop, that I can imagine, is that now main listener parses file in /etc, and if our root filesystems goes bad, it would get stuck. But unlikely you'd be able to login in that case anyway. Implementation details: - For brevity we reuse the same struct request_info. This isn't a documented feature of libwrap, but code review, viewing data in a debugger and real life testing shows that if we clear RQ_CLIENT_NAME and RQ_CLIENT_ADDR every time, it works as intended. - We set SO_LINGER on the socket to force immediate connection reset. - We log message exactly as libwrap's refuse() would do. Differential revision: https://reviews.freebsd.org/D33044
2022-01-03 02:32:30 +00:00
eval_hostaddr(req.client));
debug("Connection refused by tcp wrapper");
continue;
}
#endif /* LIBWRAP */
2022-10-04 15:10:40 +00:00
if (unset_nonblock(*newsock) == -1) {
close(*newsock);
continue;
}
if (pipe(startup_p) == -1) {
error_f("pipe(startup_p): %s", strerror(errno));
2022-02-23 18:16:45 +00:00
close(*newsock);
2006-09-30 13:38:06 +00:00
continue;
2022-02-23 18:16:45 +00:00
}
2021-04-23 19:10:38 +00:00
if (drop_connection(*newsock, startups, startup_p[0])) {
2006-09-30 13:38:06 +00:00
close(*newsock);
2021-04-23 19:10:38 +00:00
close(startup_p[0]);
close(startup_p[1]);
2006-09-30 13:38:06 +00:00
continue;
}
if (rexec_flag && socketpair(AF_UNIX,
SOCK_STREAM, 0, config_s) == -1) {
error("reexec socketpair: %s",
strerror(errno));
close(*newsock);
close(startup_p[0]);
close(startup_p[1]);
continue;
}
for (j = 0; j < options.max_startups; j++)
if (startup_pipes[j] == -1) {
startup_pipes[j] = startup_p[0];
startups++;
2020-02-14 19:47:15 +00:00
startup_flags[j] = 1;
2006-09-30 13:38:06 +00:00
break;
}
/*
* Got connection. Fork a child to handle it, unless
* we are in debugging mode.
*/
if (debug_flag) {
/*
* In debugging mode. Close the listening
* socket, and start processing the
* connection without forking.
*/
debug("Server will not fork when running in debugging mode.");
close_listen_socks();
*sock_in = *newsock;
*sock_out = *newsock;
close(startup_p[0]);
close(startup_p[1]);
startup_pipe = -1;
pid = getpid();
if (rexec_flag) {
2018-08-28 10:47:58 +00:00
send_rexec_state(config_s[0], cfg);
2006-09-30 13:38:06 +00:00
close(config_s[0]);
}
2022-02-23 18:16:45 +00:00
free(pfd);
2020-02-14 19:47:15 +00:00
return;
2006-09-30 13:38:06 +00:00
}
/*
* Normal production daemon. Fork, and have
* the child process the connection. The
* parent continues listening.
*/
platform_pre_fork();
2020-02-14 19:47:15 +00:00
listening++;
2006-09-30 13:38:06 +00:00
if ((pid = fork()) == 0) {
/*
* Child. Close the listening and
* max_startup sockets. Start using
* the accepted socket. Reinitialize
* logging (since our pid has changed).
2020-02-14 19:47:15 +00:00
* We return from this function to handle
2006-09-30 13:38:06 +00:00
* the connection.
*/
platform_post_fork_child();
startup_pipe = startup_p[1];
close_startup_pipes();
close_listen_socks();
*sock_in = *newsock;
*sock_out = *newsock;
log_init(__progname,
options.log_level,
options.log_facility,
log_stderr);
if (rexec_flag)
close(config_s[0]);
2020-02-14 19:47:15 +00:00
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);
}
2022-02-23 18:16:45 +00:00
free(pfd);
2020-02-14 19:47:15 +00:00
return;
2006-09-30 13:38:06 +00:00
}
/* Parent. Stay in the loop. */
platform_post_fork_parent(pid);
2021-02-14 21:00:25 +00:00
if (pid == -1)
2006-09-30 13:38:06 +00:00
error("fork: %.100s", strerror(errno));
else
debug("Forked child %ld.", (long)pid);
close(startup_p[1]);
if (rexec_flag) {
2021-02-14 21:09:58 +00:00
close(config_s[1]);
2018-08-28 10:47:58 +00:00
send_rexec_state(config_s[0], cfg);
2006-09-30 13:38:06 +00:00
close(config_s[0]);
}
close(*newsock);
/*
* Ensure that our random state differs
* from that of the child
*/
arc4random_stir();
2014-01-30 10:56:49 +00:00
arc4random_buf(rnd, sizeof(rnd));
2015-07-02 13:15:34 +00:00
#ifdef WITH_OPENSSL
2014-01-30 10:56:49 +00:00
RAND_seed(rnd, sizeof(rnd));
2016-01-19 10:10:58 +00:00
if ((RAND_bytes((u_char *)rnd, 1)) != 1)
fatal("%s: RAND_bytes failed", __func__);
2015-07-02 13:15:34 +00:00
#endif
2014-03-22 15:23:38 +00:00
explicit_bzero(rnd, sizeof(rnd));
2006-09-30 13:38:06 +00:00
}
}
}
2017-01-31 12:29:48 +00:00
/*
* 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"
2018-08-28 10:47:58 +00:00
* under certain circumstances, but rhosts authentication is useless
2017-01-31 12:29:48 +00:00
* 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];
2017-01-31 12:33:47 +00:00
socklen_t i, option_size = sizeof(opts), fromlen = sizeof(from);
2017-01-31 12:29:48 +00:00
char text[sizeof(opts) * 3 + 1];
memset(&from, 0, sizeof(from));
if (getpeername(sock_in, (struct sockaddr *)&from,
2021-02-14 21:00:25 +00:00
&fromlen) == -1)
2017-01-31 12:29:48 +00:00
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 */
}
2006-09-30 13:38:06 +00:00
2018-05-06 12:27:04 +00:00
/* 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));
2021-04-23 19:10:38 +00:00
debug_f("set routing domain %d (was %d)", rtable, ortable);
2018-05-06 12:27:04 +00:00
#else /* defined(__OpenBSD__) */
fatal("Unable to set routing domain: not supported in this platform");
#endif
}
2018-08-28 10:47:58 +00:00
static void
accumulate_host_timing_secret(struct sshbuf *server_cfg,
2021-02-14 21:00:25 +00:00
struct sshkey *key)
2018-08-28 10:47:58 +00:00
{
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)
2021-04-23 19:10:38 +00:00
fatal_f("ssh_digest_start");
2018-08-28 10:47:58 +00:00
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)
2021-04-23 19:10:38 +00:00
fatal_f("ssh_digest_update");
2018-08-28 10:47:58 +00:00
len = ssh_digest_bytes(SSH_DIGEST_SHA512);
hash = xmalloc(len);
if (ssh_digest_final(ctx, hash, len) != 0)
2021-04-23 19:10:38 +00:00
fatal_f("ssh_digest_final");
2018-08-28 10:47:58 +00:00
options.timing_secret = PEEK_U64(hash);
freezero(hash, len);
ssh_digest_free(ctx);
ctx = NULL;
return;
}
if ((buf = sshbuf_new()) == NULL)
2021-04-23 19:10:38 +00:00
fatal_f("could not allocate buffer");
2018-08-28 10:47:58 +00:00
if ((r = sshkey_private_serialize(key, buf)) != 0)
2023-02-05 18:04:12 +00:00
fatal_fr(r, "encode %s key", sshkey_ssh_name(key));
2018-08-28 10:47:58 +00:00
if (ssh_digest_update(ctx, sshbuf_ptr(buf), sshbuf_len(buf)) != 0)
2021-04-23 19:10:38 +00:00
fatal_f("ssh_digest_update");
2018-08-28 10:47:58 +00:00
sshbuf_reset(buf);
sshbuf_free(buf);
}
2021-02-14 21:04:52 +00:00
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;
}
2023-03-16 12:41:22 +00:00
static void
print_config(struct ssh *ssh, struct connection_info *connection_info)
{
/*
* If no connection info was provided by -C then use
* use a blank one that will cause no predicate to match.
*/
if (connection_info == NULL)
connection_info = get_connection_info(ssh, 0, 0);
connection_info->test = 1;
parse_server_match_config(&options, &includes, connection_info);
dump_config(&options);
exit(0);
}
2000-02-24 14:29:47 +00:00
/*
* Main program for the daemon.
*/
int
main(int ac, char **av)
{
2017-01-31 12:29:48 +00:00
struct ssh *ssh = NULL;
2000-02-24 14:29:47 +00:00
extern char *optarg;
extern int optind;
2023-03-16 12:41:22 +00:00
int r, opt, on = 1, do_dump_cfg = 0, already_daemon, remote_port;
2004-10-28 16:11:31 +00:00
int sock_in = -1, sock_out = -1, newsock = -1;
2018-05-06 12:27:04 +00:00
const char *remote_ip, *rdomain;
2015-07-02 13:18:50 +00:00
char *fp, *line, *laddr, *logfile = NULL;
2006-09-30 13:38:06 +00:00
int config_s[2] = { -1 , -1 };
2018-05-06 12:27:04 +00:00
u_int i, j;
2008-07-23 09:33:08 +00:00
u_int64_t ibytes, obytes;
mode_t new_umask;
2018-05-06 12:24:45 +00:00
struct sshkey *key;
struct sshkey *pubkey;
2013-09-18 17:27:38 +00:00
int keytype;
2004-02-26 10:52:33 +00:00
Authctxt *authctxt;
2018-05-06 12:27:04 +00:00
struct connection_info *connection_info = NULL;
2023-02-05 18:04:12 +00:00
sigset_t sigmask;
2000-02-24 14:29:47 +00:00
2002-06-27 22:42:11 +00:00
#ifdef HAVE_SECUREWARE
(void)set_auth_parameters(ac, av);
#endif
__progname = ssh_get_progname(av[0]);
2002-06-27 22:42:11 +00:00
2023-02-05 18:04:12 +00:00
sigemptyset(&sigmask);
sigprocmask(SIG_SETMASK, &sigmask, NULL);
2003-04-23 17:13:13 +00:00
/* Save argv. Duplicate so setproctitle emulation doesn't clobber it */
2002-06-27 22:42:11 +00:00
saved_argc = ac;
2004-10-28 16:11:31 +00:00
rexec_argc = ac;
2006-09-30 13:38:06 +00:00
saved_argv = xcalloc(ac + 1, sizeof(*saved_argv));
2018-05-06 12:27:04 +00:00
for (i = 0; (int)i < ac; i++)
2003-04-23 17:13:13 +00:00
saved_argv[i] = xstrdup(av[i]);
saved_argv[i] = NULL;
2003-04-23 17:13:13 +00:00
#ifndef HAVE_SETPROCTITLE
/* Prepare for later setproctitle emulation */
compat_init_setproctitle(ac, av);
av = saved_argv;
2003-04-23 17:13:13 +00:00
#endif
2000-02-24 14:29:47 +00:00
2004-04-20 09:46:41 +00:00
if (geteuid() == 0 && setgroups(0, NULL) == -1)
debug("setgroups(): %.200s", strerror(errno));
2006-03-22 20:41:37 +00:00
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd();
2000-02-24 14:29:47 +00:00
/* Initialize configuration options to their default values. */
initialize_server_options(&options);
/* Parse command-line arguments. */
2015-07-02 13:18:50 +00:00
while ((opt = getopt(ac, av,
2023-03-16 12:41:22 +00:00
"C:E:b:c:f:g:h:k:o:p:u:46DGQRTdeiqrtV")) != -1) {
2000-02-24 14:29:47 +00:00
switch (opt) {
case '4':
2005-06-05 15:46:09 +00:00
options.address_family = AF_INET;
2000-02-24 14:29:47 +00:00
break;
case '6':
2005-06-05 15:46:09 +00:00
options.address_family = AF_INET6;
2000-02-24 14:29:47 +00:00
break;
case 'f':
config_file_name = optarg;
break;
2010-03-08 11:19:52 +00:00
case 'c':
2018-05-06 12:27:04 +00:00
servconf_add_hostcert("[command-line]", 0,
&options, optarg);
2010-03-08 11:19:52 +00:00
break;
2000-02-24 14:29:47 +00:00
case 'd':
if (debug_flag == 0) {
debug_flag = 1;
options.log_level = SYSLOG_LEVEL_DEBUG1;
} else if (options.log_level < SYSLOG_LEVEL_DEBUG3)
options.log_level++;
2000-02-24 14:29:47 +00:00
break;
2001-05-04 04:14:23 +00:00
case 'D':
no_daemon_flag = 1;
break;
2023-03-16 12:41:22 +00:00
case 'G':
do_dump_cfg = 1;
break;
2013-09-18 17:27:38 +00:00
case 'E':
2016-03-10 20:10:25 +00:00
logfile = optarg;
2013-09-18 17:27:38 +00:00
/* FALLTHROUGH */
2001-05-04 04:14:23 +00:00
case 'e':
log_stderr = 1;
break;
2000-02-24 14:29:47 +00:00
case 'i':
inetd_flag = 1;
break;
2004-10-28 16:11:31 +00:00
case 'r':
rexec_flag = 0;
break;
case 'R':
rexeced_flag = 1;
inetd_flag = 1;
break;
2000-02-24 14:29:47 +00:00
case 'Q':
2001-05-04 04:14:23 +00:00
/* ignored */
2000-02-24 14:29:47 +00:00
break;
case 'q':
options.log_level = SYSLOG_LEVEL_QUIET;
break;
case 'b':
2017-01-31 12:33:47 +00:00
/* protocol 1, ignored */
2000-02-24 14:29:47 +00:00
break;
case 'p':
options.ports_from_cmdline = 1;
if (options.num_ports >= MAX_PORTS) {
fprintf(stderr, "too many ports.\n");
exit(1);
}
2001-05-04 04:14:23 +00:00
options.ports[options.num_ports++] = a2port(optarg);
2009-02-24 18:49:27 +00:00
if (options.ports[options.num_ports-1] <= 0) {
2001-05-04 04:14:23 +00:00
fprintf(stderr, "Bad port number.\n");
exit(1);
}
2000-02-24 14:29:47 +00:00
break;
case 'g':
2002-03-18 10:09:43 +00:00
if ((options.login_grace_time = convtime(optarg)) == -1) {
fprintf(stderr, "Invalid login grace time.\n");
exit(1);
}
2000-02-24 14:29:47 +00:00
break;
case 'k':
2017-01-31 12:33:47 +00:00
/* protocol 1, ignored */
2000-02-24 14:29:47 +00:00
break;
case 'h':
2018-05-06 12:27:04 +00:00
servconf_add_hostkey("[command-line]", 0,
2020-02-14 19:47:15 +00:00
&options, optarg, 1);
2000-02-24 14:29:47 +00:00
break;
2002-03-18 10:09:43 +00:00
case 't':
test_flag = 1;
break;
2008-07-23 09:33:08 +00:00
case 'T':
test_flag = 2;
break;
case 'C':
2020-02-14 19:47:15 +00:00
connection_info = get_connection_info(ssh, 0, 0);
2012-08-29 15:55:54 +00:00
if (parse_server_match_testspec(connection_info,
optarg) == -1)
exit(1);
2008-07-23 09:33:08 +00:00
break;
case 'u':
2015-07-02 13:15:34 +00:00
utmp_len = (u_int)strtonum(optarg, 0, HOST_NAME_MAX+1+1, NULL);
if (utmp_len > HOST_NAME_MAX+1) {
2002-10-29 10:16:02 +00:00
fprintf(stderr, "Invalid utmp length.\n");
exit(1);
}
break;
2002-03-18 10:09:43 +00:00
case 'o':
2004-02-26 10:52:33 +00:00
line = xstrdup(optarg);
if (process_server_config_line(&options, line,
2021-02-14 21:04:52 +00:00
"command-line", 0, NULL, NULL, &includes) != 0)
2002-03-18 10:09:43 +00:00
exit(1);
2013-09-18 17:27:38 +00:00
free(line);
2002-03-18 10:09:43 +00:00
break;
2023-02-05 18:04:12 +00:00
case 'V':
fprintf(stderr, "%s, %s\n",
2023-10-04 12:06:41 +00:00
SSH_RELEASE, SSH_OPENSSL_VERSION);
2023-02-05 18:04:12 +00:00
exit(0);
2000-02-24 14:29:47 +00:00
default:
2002-03-18 10:09:43 +00:00
usage();
break;
2000-02-24 14:29:47 +00:00
}
}
2004-10-28 16:11:31 +00:00
if (rexeced_flag || inetd_flag)
rexec_flag = 0;
2023-03-16 12:41:22 +00:00
if (!test_flag && !do_dump_cfg && rexec_flag && !path_absolute(av[0]))
2004-10-28 16:11:31 +00:00
fatal("sshd re-exec requires execution with an absolute path");
if (rexeced_flag)
closefrom(REEXEC_MIN_FREE_FD);
else
closefrom(REEXEC_DEVCRYPTO_RESERVED_FD);
2023-02-05 18:04:12 +00:00
seed_rng();
2013-09-18 17:27:38 +00:00
/* If requested, redirect the logs to the specified logfile. */
2016-03-10 20:10:25 +00:00
if (logfile != NULL)
2013-09-18 17:27:38 +00:00
log_redirect_stderr_to(logfile);
2000-02-24 14:29:47 +00:00
/*
* Force logging to stderr until we have loaded the private host
* key (unless started from inetd)
*/
2001-05-04 04:14:23 +00:00
log_init(__progname,
2002-03-18 10:09:43 +00:00
options.log_level == SYSLOG_LEVEL_NOT_SET ?
SYSLOG_LEVEL_INFO : options.log_level,
options.log_facility == SYSLOG_FACILITY_NOT_SET ?
SYSLOG_FACILITY_AUTH : options.log_facility,
2021-02-14 21:04:52 +00:00
log_stderr || !inetd_flag || debug_flag);
2000-02-24 14:29:47 +00:00
2004-04-20 09:46:41 +00:00
/*
* Unset KRB5CCNAME, otherwise the user's session may inherit it from
* root's environment
2005-09-03 07:04:25 +00:00
*/
2005-06-05 15:46:09 +00:00
if (getenv("KRB5CCNAME") != NULL)
2012-08-29 15:46:01 +00:00
(void) unsetenv("KRB5CCNAME");
2005-06-05 15:46:09 +00:00
2004-10-28 16:11:31 +00:00
sensitive_data.have_ssh2_key = 0;
2008-07-23 09:33:08 +00:00
/*
2018-05-06 12:27:04 +00:00
* If we're not doing an extended test do not silently ignore connection
* test params.
2008-07-23 09:33:08 +00:00
*/
2018-05-06 12:27:04 +00:00
if (test_flag < 2 && connection_info != NULL)
2008-07-23 09:33:08 +00:00
fatal("Config test connection parameter (-C) provided without "
2021-04-23 19:13:32 +00:00
"test mode (-T)");
2008-07-23 09:33:08 +00:00
2004-10-28 16:11:31 +00:00
/* Fetch our configuration */
2018-08-28 10:47:58 +00:00
if ((cfg = sshbuf_new()) == NULL)
2021-04-23 19:10:38 +00:00
fatal_f("sshbuf_new failed");
2020-02-14 19:47:15 +00:00
if (rexeced_flag) {
2021-02-14 21:09:58 +00:00
setproctitle("%s", "[rexeced]");
2018-08-28 10:47:58 +00:00
recv_rexec_state(REEXEC_CONFIG_PASS_FD, cfg);
2020-02-14 19:47:15 +00:00
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);
}
2021-02-14 21:04:52 +00:00
} else if (strcasecmp(config_file_name, "none") != 0)
2018-08-28 10:47:58 +00:00
load_server_config(config_file_name, cfg);
2004-10-28 16:11:31 +00:00
2006-09-30 13:38:06 +00:00
parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name,
2022-04-08 17:19:17 +00:00
cfg, &includes, NULL, rexeced_flag);
2000-02-24 14:29:47 +00:00
2021-04-23 19:13:32 +00:00
#ifdef WITH_OPENSSL
if (options.moduli_file != NULL)
dh_set_moduli_file(options.moduli_file);
#endif
2006-03-22 20:41:37 +00:00
2000-02-24 14:29:47 +00:00
/* Fill in default values for those options not explicitly set. */
fill_default_server_options(&options);
2013-03-22 11:19:48 +00:00
/* 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");
2015-07-02 13:18:50 +00:00
if (options.authorized_principals_command_user == NULL &&
(options.authorized_principals_command != NULL &&
strcasecmp(options.authorized_principals_command, "none") != 0))
fatal("AuthorizedPrincipalsCommand set without "
"AuthorizedPrincipalsCommandUser");
2013-03-22 11:19:48 +00:00
/*
* 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) {
2018-05-06 12:27:04 +00:00
for (i = 0; i < options.num_auth_methods; i++) {
if (auth2_methods_valid(options.auth_methods[i],
2013-03-22 11:19:48 +00:00
1) == 0)
break;
}
2018-05-06 12:27:04 +00:00
if (i >= options.num_auth_methods)
2013-03-22 11:19:48 +00:00
fatal("AuthenticationMethods cannot be satisfied by "
"enabled authentication methods");
}
2000-02-24 14:29:47 +00:00
/* Check that there are no remaining arguments. */
if (optind < ac) {
fprintf(stderr, "Extra argument %s.\n", av[optind]);
exit(1);
}
2021-04-23 19:10:38 +00:00
debug("sshd version %s, %s", SSH_VERSION, SSH_OPENSSL_VERSION);
2000-02-24 14:29:47 +00:00
2023-03-16 12:41:22 +00:00
if (do_dump_cfg)
print_config(ssh, connection_info);
2006-11-10 16:52:41 +00:00
/* Store privilege separation user for later use if required. */
2018-05-06 12:24:45 +00:00
privsep_chroot = use_privsep && (getuid() == 0 || geteuid() == 0);
2006-11-10 16:52:41 +00:00
if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) {
2018-05-06 12:24:45 +00:00
if (privsep_chroot || options.kerberos_authentication)
2006-11-10 16:52:41 +00:00
fatal("Privilege separation user %s does not exist",
SSH_PRIVSEP_USER);
} else {
privsep_pw = pwcopy(privsep_pw);
2018-05-06 12:27:04 +00:00
freezero(privsep_pw->pw_passwd, strlen(privsep_pw->pw_passwd));
2006-11-10 16:52:41 +00:00
privsep_pw->pw_passwd = xstrdup("*");
}
2006-09-30 13:38:06 +00:00
endpwent();
2013-09-18 17:27:38 +00:00
/* load host keys */
2006-09-30 13:38:06 +00:00
sensitive_data.host_keys = xcalloc(options.num_host_key_files,
2018-05-06 12:24:45 +00:00
sizeof(struct sshkey *));
2013-09-18 17:27:38 +00:00
sensitive_data.host_pubkeys = xcalloc(options.num_host_key_files,
2018-05-06 12:24:45 +00:00
sizeof(struct sshkey *));
2013-09-18 17:27:38 +00:00
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);
2015-07-02 13:15:34 +00:00
if ((r = ssh_get_authentication_socket(NULL)) == 0)
have_agent = 1;
else
2021-04-23 19:10:38 +00:00
error_r(r, "Could not connect to agent \"%s\"",
options.host_key_agent);
2013-09-18 17:27:38 +00:00
}
2001-05-04 04:14:23 +00:00
2002-03-18 10:09:43 +00:00
for (i = 0; i < options.num_host_key_files; i++) {
2020-02-14 19:47:15 +00:00
int ll = options.host_key_file_userprovided[i] ?
SYSLOG_LEVEL_ERROR : SYSLOG_LEVEL_DEBUG1;
2015-07-02 13:15:34 +00:00
if (options.host_key_files[i] == NULL)
continue;
2018-08-28 10:47:58 +00:00
if ((r = sshkey_load_private(options.host_key_files[i], "",
&key, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR)
2021-04-23 19:10:38 +00:00
do_log2_r(r, ll, "Unable to load host key \"%s\"",
options.host_key_files[i]);
2021-02-14 21:04:52 +00:00
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) {
2021-04-23 19:10:38 +00:00
do_log2_r(r, ll, "Unable to shield host key \"%s\"",
options.host_key_files[i]);
2021-02-14 21:00:25 +00:00
sshkey_free(key);
key = NULL;
}
2018-08-28 10:47:58 +00:00
if ((r = sshkey_load_public(options.host_key_files[i],
&pubkey, NULL)) != 0 && r != SSH_ERR_SYSTEM_ERROR)
2021-04-23 19:10:38 +00:00
do_log2_r(r, ll, "Unable to load host key \"%s\"",
options.host_key_files[i]);
2021-02-14 21:09:58 +00:00
if (pubkey != NULL && key != NULL) {
if (!sshkey_equal(pubkey, key)) {
error("Public key for %s does not match "
"private key", options.host_key_files[i]);
sshkey_free(pubkey);
pubkey = NULL;
}
}
if (pubkey == NULL && key != NULL) {
if ((r = sshkey_from_private(key, &pubkey)) != 0)
2021-04-23 19:10:38 +00:00
fatal_r(r, "Could not demote key: \"%s\"",
options.host_key_files[i]);
2021-02-14 21:09:58 +00:00
}
2022-10-04 15:10:40 +00:00
if (pubkey != NULL && (r = sshkey_check_rsa_length(pubkey,
options.required_rsa_size)) != 0) {
error_fr(r, "Host key %s", options.host_key_files[i]);
sshkey_free(pubkey);
sshkey_free(key);
continue;
}
2001-05-04 04:14:23 +00:00
sensitive_data.host_keys[i] = key;
2013-09-18 17:27:38 +00:00
sensitive_data.host_pubkeys[i] = pubkey;
2017-01-31 12:33:47 +00:00
if (key == NULL && pubkey != NULL && have_agent) {
2013-09-18 17:27:38 +00:00
debug("will rely on agent for hostkey %s",
options.host_key_files[i]);
keytype = pubkey->type;
} else if (key != NULL) {
keytype = key->type;
2018-08-28 10:47:58 +00:00
accumulate_host_timing_secret(cfg, key);
2013-09-18 17:27:38 +00:00
} else {
2020-02-14 19:47:15 +00:00
do_log2(ll, "Unable to load host key: %s",
2001-05-04 04:14:23 +00:00
options.host_key_files[i]);
sensitive_data.host_keys[i] = NULL;
2013-09-18 17:27:38 +00:00
sensitive_data.host_pubkeys[i] = NULL;
2001-05-04 04:14:23 +00:00
continue;
}
2013-09-18 17:27:38 +00:00
switch (keytype) {
2001-05-04 04:14:23 +00:00
case KEY_RSA:
case KEY_DSA:
2011-02-17 11:47:40 +00:00
case KEY_ECDSA:
2014-01-30 10:56:49 +00:00
case KEY_ED25519:
2021-02-14 21:04:52 +00:00
case KEY_ECDSA_SK:
case KEY_ED25519_SK:
2018-05-06 12:27:04 +00:00
case KEY_XMSS:
2015-07-02 13:15:34 +00:00
if (have_agent || key != NULL)
sensitive_data.have_ssh2_key = 1;
2001-05-04 04:14:23 +00:00
break;
}
2015-07-02 13:15:34 +00:00
if ((fp = sshkey_fingerprint(pubkey, options.fingerprint_hash,
SSH_FP_DEFAULT)) == NULL)
fatal("sshkey_fingerprint failed");
debug("%s host key #%d: %s %s",
2017-01-31 12:33:47 +00:00
key ? "private" : "agent", i, sshkey_ssh_name(pubkey), fp);
2015-07-02 13:15:34 +00:00
free(fp);
2001-05-04 04:14:23 +00:00
}
2018-08-28 10:47:58 +00:00
accumulate_host_timing_secret(cfg, NULL);
2017-01-31 12:33:47 +00:00
if (!sensitive_data.have_ssh2_key) {
logit("sshd: no hostkeys available -- exiting.");
2000-02-24 14:29:47 +00:00
exit(1);
}
2010-03-08 11:19:52 +00:00
/*
* Load certificates. They are stored in an array at identical
* indices to the public keys that they relate to.
*/
sensitive_data.host_certificates = xcalloc(options.num_host_key_files,
2018-05-06 12:24:45 +00:00
sizeof(struct sshkey *));
2010-03-08 11:19:52 +00:00
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++) {
2015-07-02 13:15:34 +00:00
if (options.host_cert_files[i] == NULL)
continue;
2018-08-28 10:47:58 +00:00
if ((r = sshkey_load_public(options.host_cert_files[i],
&key, NULL)) != 0) {
2021-04-23 19:10:38 +00:00
error_r(r, "Could not load host certificate \"%s\"",
options.host_cert_files[i]);
2010-03-08 11:19:52 +00:00
continue;
}
2018-08-28 10:47:58 +00:00
if (!sshkey_is_cert(key)) {
2010-03-08 11:19:52 +00:00
error("Certificate file is not a certificate: %s",
options.host_cert_files[i]);
2018-08-28 10:47:58 +00:00
sshkey_free(key);
2010-03-08 11:19:52 +00:00
continue;
}
/* Find matching private key */
for (j = 0; j < options.num_host_key_files; j++) {
2018-08-28 10:47:58 +00:00
if (sshkey_equal_public(key,
2021-08-30 19:14:33 +00:00
sensitive_data.host_pubkeys[j])) {
2010-03-08 11:19:52 +00:00
sensitive_data.host_certificates[j] = key;
break;
}
}
if (j >= options.num_host_key_files) {
error("No matching private key for certificate: %s",
options.host_cert_files[i]);
2018-08-28 10:47:58 +00:00
sshkey_free(key);
2010-03-08 11:19:52 +00:00
continue;
}
sensitive_data.host_certificates[j] = key;
2018-05-06 12:27:04 +00:00
debug("host certificate: #%u type %d %s", j, key->type,
2018-08-28 10:47:58 +00:00
sshkey_type(key));
2010-03-08 11:19:52 +00:00
}
2015-01-05 16:09:55 +00:00
2018-05-06 12:24:45 +00:00
if (privsep_chroot) {
struct stat st;
if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) ||
(S_ISDIR(st.st_mode) == 0))
fatal("Missing privilege separation directory: %s",
_PATH_PRIVSEP_CHROOT_DIR);
2002-10-29 10:16:02 +00:00
#ifdef HAVE_CYGWIN
if (check_ntsec(_PATH_PRIVSEP_CHROOT_DIR) &&
(st.st_uid != getuid () ||
(st.st_mode & (S_IWGRP|S_IWOTH)) != 0))
#else
if (st.st_uid != 0 || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0)
2002-10-29 10:16:02 +00:00
#endif
2003-04-23 17:13:13 +00:00
fatal("%s must be owned by root and not group or "
"world-writable.", _PATH_PRIVSEP_CHROOT_DIR);
}
2023-03-16 12:41:22 +00:00
if (test_flag > 1)
print_config(ssh, connection_info);
2008-07-23 09:33:08 +00:00
2002-03-18 10:09:43 +00:00
/* Configuration looks good, so exit if in test mode. */
if (test_flag)
exit(0);
2002-06-27 22:42:11 +00:00
/*
* Clear out any supplemental groups we may have inherited. This
* prevents inadvertent creation of files with bad modes (in the
2004-02-26 10:52:33 +00:00
* portable version at least, it's certainly possible for PAM
* to create a file, and we can't control the code in every
2002-06-27 22:42:11 +00:00
* module which might be used).
*/
if (setgroups(0, NULL) < 0)
debug("setgroups() failed: %.200s", strerror(errno));
2004-10-28 16:11:31 +00:00
if (rexec_flag) {
2018-05-06 12:27:04 +00:00
if (rexec_argc < 0)
fatal("rexec_argc %d < 0", rexec_argc);
2006-09-30 13:38:06 +00:00
rexec_argv = xcalloc(rexec_argc + 2, sizeof(char *));
2018-05-06 12:27:04 +00:00
for (i = 0; i < (u_int)rexec_argc; i++) {
2004-10-28 16:11:31 +00:00
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;
}
2021-02-14 21:04:52 +00:00
listener_proctitle = prepare_proctitle(ac, av);
2004-10-28 16:11:31 +00:00
2008-07-23 09:33:08 +00:00
/* Ensure that umask disallows at least group and world write */
new_umask = umask(0077) | 0022;
(void) umask(new_umask);
/* Initialize the log (it is reinitialized below in case we forked). */
2005-06-05 15:46:09 +00:00
if (debug_flag && (!inetd_flag || rexeced_flag))
2000-02-24 14:29:47 +00:00
log_stderr = 1;
2021-04-23 19:10:38 +00:00
log_init(__progname, options.log_level,
options.log_facility, log_stderr);
for (i = 0; i < options.num_log_verbose; i++)
log_verbose_add(options.log_verbose[i]);
2000-02-24 14:29:47 +00:00
/*
2017-01-31 12:33:47 +00:00
* 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.
*/
2017-01-31 12:33:47 +00:00
already_daemon = daemonized();
if (!(debug_flag || inetd_flag || no_daemon_flag || already_daemon)) {
2021-02-14 21:00:25 +00:00
if (daemon(0, 0) == -1)
2000-02-24 14:29:47 +00:00
fatal("daemon() failed: %.200s", strerror(errno));
2017-01-31 12:33:47 +00:00
disconnect_controlling_tty();
2000-02-24 14:29:47 +00:00
}
/* Reinitialize the log (because of the fork above). */
2001-05-04 04:14:23 +00:00
log_init(__progname, options.log_level, options.log_facility, log_stderr);
2000-02-24 14:29:47 +00:00
/* Avoid killing the process in high-pressure swapping environments. */
if (!inetd_flag && madvise(NULL, 0, MADV_PROTECT) != 0)
debug("madvise(): %.200s", strerror(errno));
2021-04-23 19:13:32 +00:00
/*
* Chdir to the root directory so that the current disk can be
* unmounted if desired.
*/
2013-09-18 17:27:38 +00:00
if (chdir("/") == -1)
error("chdir(\"/\"): %s", strerror(errno));
2002-03-18 10:09:43 +00:00
2001-05-04 04:14:23 +00:00
/* ignore SIGPIPE */
2021-02-14 21:04:52 +00:00
ssh_signal(SIGPIPE, SIG_IGN);
2000-02-24 14:29:47 +00:00
2006-09-30 13:38:06 +00:00
/* Get a connection, either from inetd or a listening TCP socket */
2000-02-24 14:29:47 +00:00
if (inetd_flag) {
2006-09-30 13:38:06 +00:00
server_accept_inetd(&sock_in, &sock_out);
2000-02-24 14:29:47 +00:00
} else {
2010-03-08 11:19:52 +00:00
platform_pre_listen();
2006-09-30 13:38:06 +00:00
server_listen();
2000-02-24 14:29:47 +00:00
2021-02-14 21:04:52 +00:00
ssh_signal(SIGHUP, sighup_handler);
ssh_signal(SIGCHLD, main_sigchld_handler);
ssh_signal(SIGTERM, sigterm_handler);
ssh_signal(SIGQUIT, sigterm_handler);
2002-03-18 10:09:43 +00:00
2006-09-30 13:38:06 +00:00
/*
* Write out the pid file after the sigterm handler
* is setup and the listen sockets are bound
*/
2015-07-02 13:15:34 +00:00
if (options.pid_file != NULL && !debug_flag) {
2006-09-30 13:38:06 +00:00
FILE *f = fopen(options.pid_file, "w");
if (f == NULL) {
error("Couldn't create pid file \"%s\": %s",
options.pid_file, strerror(errno));
} else {
fprintf(f, "%ld\n", (long) getpid());
2000-02-24 14:29:47 +00:00
fclose(f);
}
}
2006-09-30 13:38:06 +00:00
/* Accept a connection and return in a forked child */
server_accept_loop(&sock_in, &sock_out,
&newsock, config_s);
2000-02-24 14:29:47 +00:00
}
/* This is the child processing a new connection. */
2004-04-20 09:46:41 +00:00
setproctitle("%s", "[accepted]");
2000-02-24 14:29:47 +00:00
/*
* Create a new session and process group since the 4.4BSD
* setlogin() affects the entire process group. We don't
* want the child to be able to affect the parent.
*/
2021-02-14 21:00:25 +00:00
if (!debug_flag && !inetd_flag && setsid() == -1)
error("setsid: %.100s", strerror(errno));
2004-10-28 16:11:31 +00:00
if (rexec_flag) {
debug("rexec start in %d out %d newsock %d pipe %d sock %d",
sock_in, sock_out, newsock, startup_pipe, config_s[0]);
2023-03-16 12:41:22 +00:00
if (dup2(newsock, STDIN_FILENO) == -1)
debug3_f("dup2 stdin: %s", strerror(errno));
if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1)
debug3_f("dup2 stdout: %s", strerror(errno));
2004-10-28 16:11:31 +00:00
if (startup_pipe == -1)
close(REEXEC_STARTUP_PIPE_FD);
2014-01-30 10:56:49 +00:00
else if (startup_pipe != REEXEC_STARTUP_PIPE_FD) {
2023-03-16 12:41:22 +00:00
if (dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD) == -1)
debug3_f("dup2 startup_p: %s", strerror(errno));
2014-01-30 10:56:49 +00:00
close(startup_pipe);
startup_pipe = REEXEC_STARTUP_PIPE_FD;
}
2004-10-28 16:11:31 +00:00
2023-03-16 12:41:22 +00:00
if (dup2(config_s[1], REEXEC_CONFIG_PASS_FD) == -1)
debug3_f("dup2 config_s: %s", strerror(errno));
2004-10-28 16:11:31 +00:00
close(config_s[1]);
2021-02-14 21:09:58 +00:00
ssh_signal(SIGHUP, SIG_IGN); /* avoid reset to SIG_DFL */
2004-10-28 16:11:31 +00:00
execv(rexec_argv[0], rexec_argv);
/* Reexec has failed, fall back and continue */
error("rexec of %s failed: %s", rexec_argv[0], strerror(errno));
recv_rexec_state(REEXEC_CONFIG_PASS_FD, NULL);
log_init(__progname, options.log_level,
options.log_facility, log_stderr);
/* Clean up fds */
close(REEXEC_CONFIG_PASS_FD);
newsock = sock_out = sock_in = dup(STDIN_FILENO);
2021-04-23 19:10:38 +00:00
if (stdfd_devnull(1, 1, 0) == -1)
error_f("stdfd_devnull failed");
2004-10-28 16:11:31 +00:00
debug("rexec cleanup in %d out %d newsock %d pipe %d sock %d",
sock_in, sock_out, newsock, startup_pipe, config_s[0]);
}
2010-03-08 11:19:52 +00:00
/* Executed child processes don't need these. */
fcntl(sock_out, F_SETFD, FD_CLOEXEC);
fcntl(sock_in, F_SETFD, FD_CLOEXEC);
2021-02-14 21:07:21 +00:00
/* We will not restart on SIGHUP since it no longer makes sense. */
2021-02-14 21:04:52 +00:00
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);
2000-02-24 14:29:47 +00:00
#ifdef __FreeBSD__
/*
* Initialize the resolver. This may not happen automatically
* before privsep chroot().
*/
if ((_res.options & RES_INIT) == 0) {
debug("res_init()");
res_init();
}
#ifdef GSSAPI
/*
* Force GSS-API to parse its configuration and load any
* mechanism plugins.
*/
{
gss_OID_set mechs;
OM_uint32 minor_status;
gss_indicate_mechs(&minor_status, &mechs);
gss_release_oid_set(&minor_status, &mechs);
}
#endif
#endif
2000-02-24 14:29:47 +00:00
/*
* Register our connection. This turns encryption off because we do
* not have a key.
*/
2020-02-14 19:47:15 +00:00
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);
2018-05-06 12:24:45 +00:00
2017-01-31 12:29:48 +00:00
check_ip_options(ssh);
2000-02-24 14:29:47 +00:00
2018-05-06 12:24:45 +00:00
/* Prepare the channels layer */
channel_init_channels(ssh);
channel_set_af(ssh, options.address_family);
2023-02-05 18:04:12 +00:00
process_channel_timeouts(ssh, &options);
2018-05-06 12:24:45 +00:00
process_permitopen(ssh, &options);
2005-09-03 07:04:25 +00:00
/* Set SO_KEEPALIVE if requested. */
2020-02-14 19:47:15 +00:00
if (options.tcp_keep_alive && ssh_packet_connection_is_on_socket(ssh) &&
2021-02-14 21:00:25 +00:00
setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1)
2005-09-03 07:04:25 +00:00
error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
2017-01-31 12:29:48 +00:00
if ((remote_port = ssh_remote_port(ssh)) < 0) {
debug("ssh_remote_port failed");
2005-09-03 07:04:25 +00:00
cleanup_exit(255);
}
2006-03-22 20:41:37 +00:00
2018-05-06 12:27:04 +00:00
if (options.routing_domain != NULL)
set_process_rdomain(ssh, options.routing_domain);
2006-09-30 13:38:06 +00:00
/*
* The rest of the code depends on the fact that
2017-01-31 12:29:48 +00:00
* ssh_remote_ipaddr() caches the remote ip, even if
2006-09-30 13:38:06 +00:00
* the socket goes away.
*/
2017-01-31 12:29:48 +00:00
remote_ip = ssh_remote_ipaddr(ssh);
2000-02-24 14:29:47 +00:00
#ifdef HAVE_LOGIN_CAP
/* Also caches remote hostname for sandboxed child. */
auth_get_canonical_hostname(ssh, options.use_dns);
#endif
2005-06-05 15:46:09 +00:00
#ifdef SSH_AUDIT_EVENTS
audit_connection_from(remote_ip, remote_port);
#endif
2002-03-18 10:09:43 +00:00
2018-05-06 12:27:04 +00:00
rdomain = ssh_packet_rdomain_in(ssh);
2000-02-24 14:29:47 +00:00
/* Log the connection. */
2015-07-02 13:18:50 +00:00
laddr = get_local_ipaddr(sock_in);
2018-05-06 12:27:04 +00:00
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 ? "" : "\"");
2015-07-02 13:18:50 +00:00
free(laddr);
2000-02-24 14:29:47 +00:00
/*
2006-03-22 20:41:37 +00:00
* We don't want to listen forever unless the other side
2000-02-24 14:29:47 +00:00
* successfully authenticates itself. So we set up an alarm which is
* cleared after successful authentication. A limit of zero
2006-03-22 20:41:37 +00:00
* indicates no limit. Note that we don't set the alarm in debugging
2000-02-24 14:29:47 +00:00
* mode; it is just annoying to have the server exit just when you
* are about to discover the bug.
*/
2021-02-14 21:04:52 +00:00
ssh_signal(SIGALRM, grace_alarm_handler);
2000-02-24 14:29:47 +00:00
if (!debug_flag)
alarm(options.login_grace_time);
2021-02-14 21:07:21 +00:00
if ((r = kex_exchange_identification(ssh, -1,
options.version_addendum)) != 0)
sshpkt_fatal(ssh, r, "banner exchange");
2020-02-14 19:47:15 +00:00
ssh_packet_set_nonblocking(ssh);
2000-02-24 14:29:47 +00:00
2004-02-26 10:52:33 +00:00
/* allocate authentication context */
2006-09-30 13:38:06 +00:00
authctxt = xcalloc(1, sizeof(*authctxt));
2020-02-14 19:47:15 +00:00
ssh->authctxt = authctxt;
2004-02-26 10:52:33 +00:00
2018-08-28 10:47:58 +00:00
authctxt->loginmsg = loginmsg;
2005-06-05 15:46:09 +00:00
2004-02-26 10:52:33 +00:00
/* XXX global for cleanup, access from other modules */
the_authctxt = authctxt;
2018-05-06 12:27:04 +00:00
/* Set default key authentication options */
if ((auth_opts = sshauthopt_new_with_keys_defaults()) == NULL)
fatal("allocation failed");
2005-06-05 15:46:09 +00:00
/* prepare buffer to collect messages to display to user after login */
2018-08-28 10:47:58 +00:00
if ((loginmsg = sshbuf_new()) == NULL)
2021-04-23 19:10:38 +00:00
fatal_f("sshbuf_new failed");
2010-03-08 11:19:52 +00:00
auth_debug_reset();
2005-06-05 15:46:09 +00:00
BLACKLIST_INIT();
2013-09-18 17:27:38 +00:00
if (use_privsep) {
2020-02-14 19:47:15 +00:00
if (privsep_preauth(ssh) == 1)
goto authenticated;
2017-01-31 12:33:47 +00:00
} else if (have_agent) {
2015-07-02 13:15:34 +00:00
if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) {
2021-04-23 19:10:38 +00:00
error_r(r, "Unable to get agent socket");
2015-07-02 13:15:34 +00:00
have_agent = 0;
}
}
2000-02-24 14:29:47 +00:00
/* perform the key exchange */
/* authenticate user and start session */
2020-02-14 19:47:15 +00:00
do_ssh2_kex(ssh);
do_authentication2(ssh);
2017-01-31 12:33:47 +00:00
/*
* If we use privilege separation, the unprivileged child transfers
* the current keystate and exits
*/
if (use_privsep) {
2020-02-14 19:47:15 +00:00
mm_send_keystate(ssh, pmonitor);
ssh_packet_clear_keys(ssh);
exit(0);
}
authenticated:
2006-03-22 20:41:37 +00:00
/*
* Cancel the alarm we set to limit the time taken for
* authentication.
*/
alarm(0);
2021-02-14 21:04:52 +00:00
ssh_signal(SIGALRM, SIG_DFL);
2006-09-30 13:38:06 +00:00
authctxt->authenticated = 1;
2006-03-22 20:41:37 +00:00
if (startup_pipe != -1) {
close(startup_pipe);
startup_pipe = -1;
}
2005-06-05 15:46:09 +00:00
#ifdef SSH_AUDIT_EVENTS
2020-02-14 19:47:15 +00:00
audit_event(ssh, SSH_AUTH_SUCCESS);
2005-06-05 15:46:09 +00:00
#endif
#ifdef GSSAPI
if (options.gss_authentication) {
temporarily_use_uid(authctxt->pw);
ssh_gssapi_storecreds();
restore_uid();
}
#endif
#ifdef USE_PAM
if (options.use_pam) {
do_pam_setcred(1);
2018-05-06 12:27:04 +00:00
do_pam_session(ssh);
}
#endif
/*
* In privilege separation, we fork another child and prepare
* file descriptor passing.
*/
if (use_privsep) {
2020-02-14 19:47:15 +00:00
privsep_postauth(ssh, authctxt);
/* the monitor process [priv] will not return */
}
2020-02-14 19:47:15 +00:00
ssh_packet_set_timeout(ssh, options.client_alive_interval,
2008-07-23 09:33:08 +00:00
options.client_alive_count_max);
2015-07-02 13:15:34 +00:00
/* Try to send all our hostkeys to the client */
2018-05-06 12:24:45 +00:00
notify_hostkeys(ssh);
2015-07-02 13:15:34 +00:00
2004-02-26 10:52:33 +00:00
/* Start session. */
2018-05-06 12:24:45 +00:00
do_authenticated(ssh, authctxt);
2000-02-24 14:29:47 +00:00
/* The connection has been terminated. */
2020-02-14 19:47:15 +00:00
ssh_packet_get_bytes(ssh, &ibytes, &obytes);
2011-02-17 11:47:40 +00:00
verbose("Transferred: sent %llu, received %llu bytes",
(unsigned long long)obytes, (unsigned long long)ibytes);
2008-07-23 09:33:08 +00:00
verbose("Closing connection to %.500s port %d", remote_ip, remote_port);
#ifdef USE_PAM
if (options.use_pam)
finish_pam();
#endif /* USE_PAM */
2005-06-05 15:46:09 +00:00
#ifdef SSH_AUDIT_EVENTS
2020-02-14 19:47:15 +00:00
PRIVSEP(audit_event(ssh, SSH_CONNECTION_CLOSE));
2005-06-05 15:46:09 +00:00
#endif
2020-02-14 19:47:15 +00:00
ssh_packet_close(ssh);
if (use_privsep)
mm_terminate();
2000-02-24 14:29:47 +00:00
exit(0);
}
2015-07-02 13:15:34 +00:00
int
2020-02-14 19:47:15 +00:00
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)
2013-09-18 17:27:38 +00:00
{
2015-07-02 13:15:34 +00:00
int r;
2020-02-14 19:47:15 +00:00
if (use_privsep) {
if (privkey) {
if (mm_sshkey_sign(ssh, privkey, signature, slenp,
2021-02-14 21:09:58 +00:00
data, dlen, alg, options.sk_provider, NULL,
2021-02-14 21:04:52 +00:00
ssh->compat) < 0)
2021-04-23 19:10:38 +00:00
fatal_f("privkey sign failed");
2020-02-14 19:47:15 +00:00
} else {
if (mm_sshkey_sign(ssh, pubkey, signature, slenp,
2021-02-14 21:09:58 +00:00
data, dlen, alg, options.sk_provider, NULL,
2021-02-14 21:04:52 +00:00
ssh->compat) < 0)
2021-04-23 19:10:38 +00:00
fatal_f("pubkey sign failed");
2020-02-14 19:47:15 +00:00
}
2013-09-18 17:27:38 +00:00
} else {
2020-02-14 19:47:15 +00:00
if (privkey) {
if (sshkey_sign(privkey, signature, slenp, data, dlen,
2021-02-14 21:09:58 +00:00
alg, options.sk_provider, NULL, ssh->compat) < 0)
2021-04-23 19:10:38 +00:00
fatal_f("privkey sign failed");
2020-02-14 19:47:15 +00:00
} else {
if ((r = ssh_agent_sign(auth_sock, pubkey,
signature, slenp, data, dlen, alg,
ssh->compat)) != 0) {
2021-04-23 19:10:38 +00:00
fatal_fr(r, "agent sign failed");
2020-02-14 19:47:15 +00:00
}
}
2013-09-18 17:27:38 +00:00
}
2015-07-02 13:15:34 +00:00
return 0;
2013-09-18 17:27:38 +00:00
}
2015-08-26 09:25:17 +00:00
/* SSH2 key exchange */
2002-03-18 10:09:43 +00:00
static void
2020-02-14 19:47:15 +00:00
do_ssh2_kex(struct ssh *ssh)
2000-02-24 14:29:47 +00:00
{
2023-03-16 12:41:22 +00:00
char *hkalgs = NULL, *myproposal[PROPOSAL_MAX];
const char *compression = NULL;
2015-07-02 13:15:34 +00:00
struct kex *kex;
int r;
2000-03-09 14:52:31 +00:00
2013-09-18 17:27:38 +00:00
if (options.rekey_limit || options.rekey_interval)
2020-02-14 19:47:15 +00:00
ssh_packet_set_rekey_limits(ssh, options.rekey_limit,
2017-08-03 10:10:20 +00:00
options.rekey_interval);
2013-09-18 17:27:38 +00:00
2023-03-16 12:41:22 +00:00
if (options.compression == COMP_NONE)
compression = "none";
hkalgs = list_hostkey_types();
kex_proposal_populate_entries(ssh, myproposal, options.kex_algorithms,
options.ciphers, options.macs, compression, hkalgs);
free(hkalgs);
2001-05-04 04:14:23 +00:00
/* start key exchange */
2020-02-14 19:47:15 +00:00
if ((r = kex_setup(ssh, myproposal)) != 0)
2021-04-23 19:10:38 +00:00
fatal_r(r, "kex_setup");
2023-12-18 15:59:40 +00:00
kex_set_server_sig_algs(ssh, options.pubkey_accepted_algos);
2020-02-14 19:47:15 +00:00
kex = ssh->kex;
2023-12-18 15:59:40 +00:00
2015-01-05 16:09:55 +00:00
#ifdef WITH_OPENSSL
2020-02-14 19:47:15 +00:00
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;
2003-04-23 17:13:13 +00:00
kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
2006-09-30 13:38:06 +00:00
kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
2015-07-02 13:15:34 +00:00
# ifdef OPENSSL_HAS_ECC
2020-02-14 19:47:15 +00:00
kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
2015-07-02 13:15:34 +00:00
# endif
2015-01-05 16:09:55 +00:00
#endif
2020-02-14 19:47:15 +00:00
kex->kex[KEX_C25519_SHA256] = kex_gen_server;
2021-04-23 19:10:38 +00:00
kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
2010-03-08 11:19:52 +00:00
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;
2013-09-18 17:27:38 +00:00
kex->sign = sshd_hostkey_sign;
2020-02-14 19:47:15 +00:00
ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &kex->done);
#ifdef DEBUG_KEXDH
/* send 1st encrypted/maced/compressed message */
2021-02-14 21:09:58 +00:00
if ((r = sshpkt_start(ssh, SSH2_MSG_IGNORE)) != 0 ||
(r = sshpkt_put_cstring(ssh, "markus")) != 0 ||
(r = sshpkt_send(ssh)) != 0 ||
(r = ssh_packet_write_wait(ssh)) != 0)
2021-04-23 19:10:38 +00:00
fatal_fr(r, "send test");
#endif
2023-03-16 12:41:22 +00:00
kex_proposal_free_entries(myproposal);
2001-05-04 04:14:23 +00:00
debug("KEX done");
}
2004-02-26 10:52:33 +00:00
/* server specific fatal cleanup */
void
cleanup_exit(int i)
{
2020-02-14 19:47:15 +00:00
if (the_active_state != NULL && the_authctxt != NULL) {
do_cleanup(the_active_state, the_authctxt);
2015-01-05 16:09:55 +00:00
if (use_privsep && privsep_is_preauth &&
pmonitor != NULL && pmonitor->m_pid > 1) {
2012-08-29 15:46:01 +00:00
debug("Killing privsep child %d", pmonitor->m_pid);
if (kill(pmonitor->m_pid, SIGKILL) != 0 &&
2021-04-23 19:10:38 +00:00
errno != ESRCH) {
error_f("kill(%d): %s", pmonitor->m_pid,
strerror(errno));
}
2012-08-29 15:46:01 +00:00
}
}
2005-06-05 15:46:09 +00:00
#ifdef SSH_AUDIT_EVENTS
/* done after do_cleanup so it can cancel the PAM auth 'thread' */
2020-02-14 19:47:15 +00:00
if (the_active_state != NULL && (!use_privsep || mm_is_monitor()))
audit_event(the_active_state, SSH_CONNECTION_ABANDON);
2005-06-05 15:46:09 +00:00
#endif
2004-02-26 10:52:33 +00:00
_exit(i);
}