2024-09-09 02:39:57 +00:00
|
|
|
/* $OpenBSD: sshd-session.c,v 1.9 2024/09/09 02:39:57 djm Exp $ */
|
2024-05-17 00:30:23 +00:00
|
|
|
/*
|
|
|
|
* SSH2 implementation:
|
|
|
|
* Privilege Separation:
|
|
|
|
*
|
|
|
|
* Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved.
|
|
|
|
* Copyright (c) 2002 Niels Provos. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "includes.h"
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#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"
|
|
|
|
#include "openbsd-compat/sys-queue.h"
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <netdb.h>
|
|
|
|
#ifdef HAVE_PATHS_H
|
|
|
|
# include <paths.h>
|
|
|
|
#endif
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <grp.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
|
|
#ifdef WITH_OPENSSL
|
|
|
|
#include <openssl/bn.h>
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
#include <openssl/rand.h>
|
|
|
|
#include "openbsd-compat/openssl-compat.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_SECUREWARE
|
|
|
|
#include <sys/security.h>
|
|
|
|
#include <prot.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "xmalloc.h"
|
|
|
|
#include "ssh.h"
|
|
|
|
#include "ssh2.h"
|
|
|
|
#include "sshpty.h"
|
|
|
|
#include "packet.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "sshbuf.h"
|
|
|
|
#include "misc.h"
|
|
|
|
#include "match.h"
|
|
|
|
#include "servconf.h"
|
|
|
|
#include "uidswap.h"
|
|
|
|
#include "compat.h"
|
|
|
|
#include "cipher.h"
|
|
|
|
#include "digest.h"
|
|
|
|
#include "sshkey.h"
|
|
|
|
#include "kex.h"
|
|
|
|
#include "authfile.h"
|
|
|
|
#include "pathnames.h"
|
|
|
|
#include "atomicio.h"
|
|
|
|
#include "canohost.h"
|
|
|
|
#include "hostfile.h"
|
|
|
|
#include "auth.h"
|
|
|
|
#include "authfd.h"
|
|
|
|
#include "msg.h"
|
|
|
|
#include "dispatch.h"
|
|
|
|
#include "channels.h"
|
|
|
|
#include "session.h"
|
|
|
|
#include "monitor.h"
|
|
|
|
#ifdef GSSAPI
|
|
|
|
#include "ssh-gss.h"
|
|
|
|
#endif
|
|
|
|
#include "monitor_wrap.h"
|
|
|
|
#include "auth-options.h"
|
|
|
|
#include "version.h"
|
|
|
|
#include "ssherr.h"
|
|
|
|
#include "sk-api.h"
|
|
|
|
#include "srclimit.h"
|
|
|
|
#include "dh.h"
|
|
|
|
|
|
|
|
/* Re-exec fds */
|
|
|
|
#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1)
|
|
|
|
#define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2)
|
|
|
|
#define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3)
|
|
|
|
#define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4)
|
|
|
|
|
2024-10-14 01:57:50 +00:00
|
|
|
/* Privsep fds */
|
|
|
|
#define PRIVSEP_MONITOR_FD (STDERR_FILENO + 1)
|
|
|
|
#define PRIVSEP_LOG_FD (STDERR_FILENO + 2)
|
|
|
|
#define PRIVSEP_MIN_FREE_FD (STDERR_FILENO + 3)
|
|
|
|
|
2024-05-17 00:30:23 +00:00
|
|
|
extern char *__progname;
|
|
|
|
|
|
|
|
/* Server configuration options. */
|
|
|
|
ServerOptions options;
|
|
|
|
|
|
|
|
/* Name of the server configuration file. */
|
|
|
|
char *config_file_name = _PATH_SERVER_CONFIG_FILE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Debug mode flag. This can be set on the command line. If debug
|
|
|
|
* mode is enabled, extra debugging output will be sent to the system
|
|
|
|
* log, the daemon will not go to background, and will exit after processing
|
|
|
|
* the first connection.
|
|
|
|
*/
|
|
|
|
int debug_flag = 0;
|
|
|
|
|
|
|
|
/* Flag indicating that the daemon is being started from inetd. */
|
|
|
|
static int inetd_flag = 0;
|
|
|
|
|
|
|
|
/* debug goes to stderr unless inetd_flag is set */
|
|
|
|
static int log_stderr = 0;
|
|
|
|
|
|
|
|
/* Saved arguments to main(). */
|
|
|
|
static char **saved_argv;
|
|
|
|
static int saved_argc;
|
|
|
|
|
|
|
|
/* Daemon's agent connection */
|
|
|
|
int auth_sock = -1;
|
|
|
|
static int have_agent = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Any really sensitive data in the application is contained in this
|
|
|
|
* structure. The idea is that this structure could be locked into memory so
|
|
|
|
* that the pages do not get written into swap. However, there are some
|
|
|
|
* problems. The private key contains BIGNUMs, and we do not (in principle)
|
|
|
|
* have access to the internals of them, and locking just the structure is
|
|
|
|
* not very useful. Currently, memory locking is not implemented.
|
|
|
|
*/
|
|
|
|
struct {
|
|
|
|
u_int num_hostkeys;
|
|
|
|
struct sshkey **host_keys; /* all private host keys */
|
|
|
|
struct sshkey **host_pubkeys; /* all public host keys */
|
|
|
|
struct sshkey **host_certificates; /* all public host certificates */
|
|
|
|
} sensitive_data;
|
|
|
|
|
|
|
|
/* record remote hostname or ip */
|
|
|
|
u_int utmp_len = HOST_NAME_MAX+1;
|
|
|
|
|
|
|
|
static int startup_pipe = -1; /* in child */
|
|
|
|
|
|
|
|
/* variables used for privilege separation */
|
|
|
|
struct monitor *pmonitor = NULL;
|
|
|
|
int privsep_is_preauth = 1;
|
|
|
|
static int privsep_chroot = 1;
|
|
|
|
|
|
|
|
/* Unprivileged user */
|
|
|
|
struct passwd *privsep_pw = NULL;
|
|
|
|
|
|
|
|
/* global connection state and authentication contexts */
|
|
|
|
Authctxt *the_authctxt = NULL;
|
|
|
|
struct ssh *the_active_state;
|
|
|
|
|
|
|
|
/* global key/cert auth options. XXX move to permanent ssh->authctxt? */
|
|
|
|
struct sshauthopt *auth_opts = NULL;
|
|
|
|
|
|
|
|
/* sshd_config buffer */
|
|
|
|
struct sshbuf *cfg;
|
|
|
|
|
|
|
|
/* Included files from the configuration file */
|
|
|
|
struct include_list includes = TAILQ_HEAD_INITIALIZER(includes);
|
|
|
|
|
|
|
|
/* message to be displayed after login */
|
|
|
|
struct sshbuf *loginmsg;
|
|
|
|
|
|
|
|
/* Prototypes for various functions defined later in this file. */
|
|
|
|
void destroy_sensitive_data(void);
|
|
|
|
void demote_sensitive_data(void);
|
2024-10-14 01:57:50 +00:00
|
|
|
|
|
|
|
/* XXX reduce to stub once postauth split */
|
|
|
|
int
|
|
|
|
mm_is_monitor(void)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* m_pid is only set in the privileged part, and
|
|
|
|
* points to the unprivileged child.
|
|
|
|
*/
|
|
|
|
return (pmonitor && pmonitor->m_pid > 0);
|
|
|
|
}
|
2024-05-17 00:30:23 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Signal handler for the alarm after the login grace period has expired.
|
2024-06-26 23:16:52 +00:00
|
|
|
* As usual, this may only take signal-safe actions, even though it is
|
|
|
|
* terminal.
|
2024-05-17 00:30:23 +00:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
grace_alarm_handler(int sig)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Try to kill any processes that we have spawned, E.g. authorized
|
|
|
|
* keys command helpers or privsep children.
|
|
|
|
*/
|
|
|
|
if (getpgid(0) == getpid()) {
|
2024-06-26 23:16:52 +00:00
|
|
|
struct sigaction sa;
|
|
|
|
|
|
|
|
/* mask all other signals while in handler */
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
sa.sa_handler = SIG_IGN;
|
|
|
|
sigfillset(&sa.sa_mask);
|
2024-07-25 07:59:35 +00:00
|
|
|
#if defined(SA_RESTART)
|
2024-06-26 23:16:52 +00:00
|
|
|
sa.sa_flags = SA_RESTART;
|
2024-07-25 07:59:35 +00:00
|
|
|
#endif
|
2024-06-26 23:16:52 +00:00
|
|
|
(void)sigaction(SIGTERM, &sa, NULL);
|
2024-05-17 00:30:23 +00:00
|
|
|
kill(0, SIGTERM);
|
|
|
|
}
|
upstream: Add a facility to sshd(8) to penalise particular
problematic client behaviours, controlled by two new sshd_config(5) options:
PerSourcePenalties and PerSourcePenaltyExemptList.
When PerSourcePenalties are enabled, sshd(8) will monitor the exit
status of its child pre-auth session processes. Through the exit
status, it can observe situations where the session did not
authenticate as expected. These conditions include when the client
repeatedly attempted authentication unsucessfully (possibly indicating
an attack against one or more accounts, e.g. password guessing), or
when client behaviour caused sshd to crash (possibly indicating
attempts to exploit sshd).
When such a condition is observed, sshd will record a penalty of some
duration (e.g. 30 seconds) against the client's address. If this time
is above a minimum threshold specified by the PerSourcePenalties, then
connections from the client address will be refused (along with any
others in the same PerSourceNetBlockSize CIDR range).
Repeated offenses by the same client address will accrue greater
penalties, up to a configurable maximum. A PerSourcePenaltyExemptList
option allows certain address ranges to be exempt from all penalties.
We hope these options will make it significantly more difficult for
attackers to find accounts with weak/guessable passwords or exploit
bugs in sshd(8) itself.
PerSourcePenalties is off by default, but we expect to enable it
automatically in the near future.
much feedback markus@ and others, ok markus@
OpenBSD-Commit-ID: 89ded70eccb2b4926ef0366a4d58a693de366cca
2024-06-06 17:15:25 +00:00
|
|
|
_exit(EXIT_LOGIN_GRACE);
|
2024-05-17 00:30:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Destroy the host and server keys. They will no longer be needed. */
|
|
|
|
void
|
|
|
|
destroy_sensitive_data(void)
|
|
|
|
{
|
|
|
|
u_int i;
|
|
|
|
|
|
|
|
for (i = 0; i < options.num_host_key_files; i++) {
|
|
|
|
if (sensitive_data.host_keys[i]) {
|
|
|
|
sshkey_free(sensitive_data.host_keys[i]);
|
|
|
|
sensitive_data.host_keys[i] = NULL;
|
|
|
|
}
|
|
|
|
if (sensitive_data.host_certificates[i]) {
|
|
|
|
sshkey_free(sensitive_data.host_certificates[i]);
|
|
|
|
sensitive_data.host_certificates[i] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Demote private to public keys for network child */
|
|
|
|
void
|
|
|
|
demote_sensitive_data(void)
|
|
|
|
{
|
|
|
|
struct sshkey *tmp;
|
|
|
|
u_int i;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
for (i = 0; i < options.num_host_key_files; i++) {
|
|
|
|
if (sensitive_data.host_keys[i]) {
|
|
|
|
if ((r = sshkey_from_private(
|
|
|
|
sensitive_data.host_keys[i], &tmp)) != 0)
|
|
|
|
fatal_r(r, "could not demote host %s key",
|
|
|
|
sshkey_type(sensitive_data.host_keys[i]));
|
|
|
|
sshkey_free(sensitive_data.host_keys[i]);
|
|
|
|
sensitive_data.host_keys[i] = tmp;
|
|
|
|
}
|
|
|
|
/* Certs do not need demotion */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
reseed_prngs(void)
|
|
|
|
{
|
|
|
|
u_int32_t rnd[256];
|
|
|
|
|
|
|
|
#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));
|
|
|
|
}
|
|
|
|
|
2024-10-14 01:57:50 +00:00
|
|
|
struct sshbuf *
|
|
|
|
pack_hostkeys(void)
|
2024-05-17 00:30:23 +00:00
|
|
|
{
|
2024-10-14 01:57:50 +00:00
|
|
|
struct sshbuf *keybuf = NULL, *hostkeys = NULL;
|
|
|
|
int r;
|
|
|
|
u_int i;
|
2024-05-17 00:30:23 +00:00
|
|
|
|
2024-10-14 01:57:50 +00:00
|
|
|
if ((hostkeys = sshbuf_new()) == NULL)
|
|
|
|
fatal_f("sshbuf_new failed");
|
2024-05-17 00:30:23 +00:00
|
|
|
|
2024-10-14 01:57:50 +00:00
|
|
|
/* pack hostkeys into a string. Empty key slots get empty strings */
|
|
|
|
for (i = 0; i < options.num_host_key_files; i++) {
|
|
|
|
/* public key */
|
|
|
|
if (sensitive_data.host_pubkeys[i] != NULL) {
|
|
|
|
if ((r = sshkey_puts(sensitive_data.host_pubkeys[i],
|
|
|
|
hostkeys)) != 0)
|
|
|
|
fatal_fr(r, "compose hostkey public");
|
|
|
|
} else {
|
|
|
|
if ((r = sshbuf_put_string(hostkeys, NULL, 0)) != 0)
|
|
|
|
fatal_fr(r, "compose hostkey empty public");
|
|
|
|
}
|
|
|
|
/* cert */
|
|
|
|
if (sensitive_data.host_certificates[i] != NULL) {
|
|
|
|
if ((r = sshkey_puts(
|
|
|
|
sensitive_data.host_certificates[i],
|
|
|
|
hostkeys)) != 0)
|
|
|
|
fatal_fr(r, "compose host cert");
|
|
|
|
} else {
|
|
|
|
if ((r = sshbuf_put_string(hostkeys, NULL, 0)) != 0)
|
|
|
|
fatal_fr(r, "compose host cert empty");
|
|
|
|
}
|
2024-05-17 00:30:23 +00:00
|
|
|
}
|
2024-10-14 01:57:50 +00:00
|
|
|
|
|
|
|
sshbuf_free(keybuf);
|
|
|
|
return hostkeys;
|
2024-05-17 00:30:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
privsep_preauth(struct ssh *ssh)
|
|
|
|
{
|
|
|
|
int status, r;
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
/* Set up unprivileged child process to deal with network data */
|
|
|
|
pmonitor = monitor_init();
|
|
|
|
/* Store a pointer to the kex for later rekeying */
|
|
|
|
pmonitor->m_pkex = &ssh->kex;
|
|
|
|
|
2024-10-14 01:57:50 +00:00
|
|
|
if ((pid = fork()) == -1)
|
2024-05-17 00:30:23 +00:00
|
|
|
fatal("fork of unprivileged child failed");
|
2024-10-14 01:57:50 +00:00
|
|
|
else if (pid != 0) {
|
2024-05-17 00:30:23 +00:00
|
|
|
debug2("Network child is on pid %ld", (long)pid);
|
|
|
|
|
|
|
|
pmonitor->m_pid = pid;
|
|
|
|
if (have_agent) {
|
|
|
|
r = ssh_get_authentication_socket(&auth_sock);
|
|
|
|
if (r != 0) {
|
|
|
|
error_r(r, "Could not get agent socket");
|
|
|
|
have_agent = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
monitor_child_preauth(ssh, pmonitor);
|
|
|
|
|
|
|
|
/* Wait for the child's exit status */
|
|
|
|
while (waitpid(pid, &status, 0) == -1) {
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
|
|
|
pmonitor->m_pid = -1;
|
|
|
|
fatal_f("waitpid: %s", strerror(errno));
|
|
|
|
}
|
|
|
|
privsep_is_preauth = 0;
|
|
|
|
pmonitor->m_pid = -1;
|
|
|
|
if (WIFEXITED(status)) {
|
|
|
|
if (WEXITSTATUS(status) != 0)
|
|
|
|
fatal_f("preauth child exited with status %d",
|
|
|
|
WEXITSTATUS(status));
|
|
|
|
} else if (WIFSIGNALED(status))
|
|
|
|
fatal_f("preauth child terminated by signal %d",
|
|
|
|
WTERMSIG(status));
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
/* child */
|
|
|
|
close(pmonitor->m_sendfd);
|
|
|
|
close(pmonitor->m_log_recvfd);
|
|
|
|
|
2024-10-14 01:57:50 +00:00
|
|
|
/*
|
|
|
|
* Arrange unpriv-preauth child process fds:
|
|
|
|
* 0, 1 network socket
|
|
|
|
* 2 optional stderr
|
|
|
|
* 3 reserved
|
|
|
|
* 4 monitor message socket
|
|
|
|
* 5 monitor logging socket
|
|
|
|
*
|
|
|
|
* We know that the monitor sockets will have fds > 4 because
|
|
|
|
* of the reserved fds in main()
|
|
|
|
*/
|
2024-05-17 00:30:23 +00:00
|
|
|
|
2024-10-14 01:57:50 +00:00
|
|
|
if (ssh_packet_get_connection_in(ssh) != STDIN_FILENO &&
|
|
|
|
dup2(ssh_packet_get_connection_in(ssh), STDIN_FILENO) == -1)
|
|
|
|
fatal("dup2 stdin failed: %s", strerror(errno));
|
|
|
|
if (ssh_packet_get_connection_out(ssh) != STDOUT_FILENO &&
|
|
|
|
dup2(ssh_packet_get_connection_out(ssh),
|
|
|
|
STDOUT_FILENO) == -1)
|
|
|
|
fatal("dup2 stdout failed: %s", strerror(errno));
|
|
|
|
/* leave stderr as-is */
|
|
|
|
log_redirect_stderr_to(NULL); /* dup can clobber log fd */
|
|
|
|
if (pmonitor->m_recvfd != PRIVSEP_MONITOR_FD &&
|
|
|
|
dup2(pmonitor->m_recvfd, PRIVSEP_MONITOR_FD) == -1)
|
|
|
|
fatal("dup2 monitor fd: %s", strerror(errno));
|
|
|
|
if (pmonitor->m_log_sendfd != PRIVSEP_LOG_FD &&
|
|
|
|
dup2(pmonitor->m_log_sendfd, PRIVSEP_LOG_FD) == -1)
|
|
|
|
fatal("dup2 log fd: %s", strerror(errno));
|
|
|
|
closefrom(PRIVSEP_MIN_FREE_FD);
|
|
|
|
|
|
|
|
saved_argv[0] = options.sshd_auth_path;
|
|
|
|
execv(options.sshd_auth_path, saved_argv);
|
|
|
|
|
|
|
|
fatal_f("exec of %s failed: %s",
|
|
|
|
options.sshd_auth_path, strerror(errno));
|
2024-05-17 00:30:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
privsep_postauth(struct ssh *ssh, Authctxt *authctxt)
|
|
|
|
{
|
2024-06-13 04:35:25 +00:00
|
|
|
int skip_privdrop = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Hack for systems that don't support FD passing: retain privileges
|
|
|
|
* in the post-auth privsep process so it can allocate PTYs directly.
|
|
|
|
* This is basically equivalent to what we did <= 9.7, which was to
|
|
|
|
* disable post-auth privsep entriely.
|
|
|
|
* Cygwin doesn't need to drop privs here although it doesn't support
|
|
|
|
* fd passing, as AFAIK PTY allocation on this platform doesn't require
|
|
|
|
* special privileges to begin with.
|
|
|
|
*/
|
|
|
|
#if defined(DISABLE_FD_PASSING) && !defined(HAVE_CYGWIN)
|
|
|
|
skip_privdrop = 1;
|
|
|
|
#endif
|
|
|
|
|
2024-05-17 00:30:23 +00:00
|
|
|
/* New socket pair */
|
|
|
|
monitor_reinit(pmonitor);
|
|
|
|
|
|
|
|
pmonitor->m_pid = fork();
|
|
|
|
if (pmonitor->m_pid == -1)
|
|
|
|
fatal("fork of unprivileged child failed");
|
|
|
|
else if (pmonitor->m_pid != 0) {
|
|
|
|
verbose("User child is on pid %ld", (long)pmonitor->m_pid);
|
|
|
|
sshbuf_reset(loginmsg);
|
|
|
|
monitor_clear_keystate(ssh, pmonitor);
|
|
|
|
monitor_child_postauth(ssh, pmonitor);
|
|
|
|
|
|
|
|
/* NEVERREACHED */
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* child */
|
|
|
|
|
|
|
|
close(pmonitor->m_sendfd);
|
|
|
|
pmonitor->m_sendfd = -1;
|
|
|
|
|
|
|
|
/* Demote the private keys to public keys. */
|
|
|
|
demote_sensitive_data();
|
|
|
|
|
|
|
|
reseed_prngs();
|
|
|
|
|
|
|
|
/* Drop privileges */
|
2024-06-13 04:35:25 +00:00
|
|
|
if (!skip_privdrop)
|
|
|
|
do_setusercontext(authctxt->pw);
|
2024-05-17 00:30:23 +00:00
|
|
|
|
|
|
|
/* It is safe now to apply the key state */
|
|
|
|
monitor_apply_keystate(ssh, pmonitor);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tell the packet layer that authentication was successful, since
|
|
|
|
* this information is not part of the key state.
|
|
|
|
*/
|
|
|
|
ssh_packet_set_authenticated(ssh);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sshkey *
|
|
|
|
get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
|
|
|
|
{
|
|
|
|
u_int i;
|
|
|
|
struct sshkey *key;
|
|
|
|
|
|
|
|
for (i = 0; i < options.num_host_key_files; i++) {
|
|
|
|
switch (type) {
|
|
|
|
case KEY_RSA_CERT:
|
|
|
|
case KEY_DSA_CERT:
|
|
|
|
case KEY_ECDSA_CERT:
|
|
|
|
case KEY_ED25519_CERT:
|
|
|
|
case KEY_ECDSA_SK_CERT:
|
|
|
|
case KEY_ED25519_SK_CERT:
|
|
|
|
case KEY_XMSS_CERT:
|
|
|
|
key = sensitive_data.host_certificates[i];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
key = sensitive_data.host_keys[i];
|
|
|
|
if (key == NULL && !need_private)
|
|
|
|
key = sensitive_data.host_pubkeys[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (key == NULL || key->type != type)
|
|
|
|
continue;
|
|
|
|
switch (type) {
|
|
|
|
case KEY_ECDSA:
|
|
|
|
case KEY_ECDSA_SK:
|
|
|
|
case KEY_ECDSA_CERT:
|
|
|
|
case KEY_ECDSA_SK_CERT:
|
|
|
|
if (key->ecdsa_nid != nid)
|
|
|
|
continue;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
default:
|
|
|
|
return need_private ?
|
|
|
|
sensitive_data.host_keys[i] : key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sshkey *
|
|
|
|
get_hostkey_public_by_type(int type, int nid, struct ssh *ssh)
|
|
|
|
{
|
|
|
|
return get_hostkey_by_type(type, nid, 0, ssh);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sshkey *
|
|
|
|
get_hostkey_private_by_type(int type, int nid, struct ssh *ssh)
|
|
|
|
{
|
|
|
|
return get_hostkey_by_type(type, nid, 1, ssh);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sshkey *
|
|
|
|
get_hostkey_by_index(int ind)
|
|
|
|
{
|
|
|
|
if (ind < 0 || (u_int)ind >= options.num_host_key_files)
|
|
|
|
return (NULL);
|
|
|
|
return (sensitive_data.host_keys[ind]);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sshkey *
|
|
|
|
get_hostkey_public_by_index(int ind, struct ssh *ssh)
|
|
|
|
{
|
|
|
|
if (ind < 0 || (u_int)ind >= options.num_host_key_files)
|
|
|
|
return (NULL);
|
|
|
|
return (sensitive_data.host_pubkeys[ind]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
get_hostkey_index(struct sshkey *key, int compare, struct ssh *ssh)
|
|
|
|
{
|
|
|
|
u_int i;
|
|
|
|
|
|
|
|
for (i = 0; i < options.num_host_key_files; i++) {
|
|
|
|
if (sshkey_is_cert(key)) {
|
|
|
|
if (key == sensitive_data.host_certificates[i] ||
|
|
|
|
(compare && sensitive_data.host_certificates[i] &&
|
|
|
|
sshkey_equal(key,
|
|
|
|
sensitive_data.host_certificates[i])))
|
|
|
|
return (i);
|
|
|
|
} else {
|
|
|
|
if (key == sensitive_data.host_keys[i] ||
|
|
|
|
(compare && sensitive_data.host_keys[i] &&
|
|
|
|
sshkey_equal(key, sensitive_data.host_keys[i])))
|
|
|
|
return (i);
|
|
|
|
if (key == sensitive_data.host_pubkeys[i] ||
|
|
|
|
(compare && sensitive_data.host_pubkeys[i] &&
|
|
|
|
sshkey_equal(key, sensitive_data.host_pubkeys[i])))
|
|
|
|
return (i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Inform the client of all hostkeys */
|
|
|
|
static void
|
|
|
|
notify_hostkeys(struct ssh *ssh)
|
|
|
|
{
|
|
|
|
struct sshbuf *buf;
|
|
|
|
struct sshkey *key;
|
|
|
|
u_int i, nkeys;
|
|
|
|
int r;
|
|
|
|
char *fp;
|
|
|
|
|
|
|
|
/* Some clients cannot cope with the hostkeys message, skip those. */
|
|
|
|
if (ssh->compat & SSH_BUG_HOSTKEYS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ((buf = sshbuf_new()) == NULL)
|
|
|
|
fatal_f("sshbuf_new");
|
|
|
|
for (i = nkeys = 0; i < options.num_host_key_files; i++) {
|
|
|
|
key = get_hostkey_public_by_index(i, ssh);
|
|
|
|
if (key == NULL || key->type == KEY_UNSPEC ||
|
|
|
|
sshkey_is_cert(key))
|
|
|
|
continue;
|
|
|
|
fp = sshkey_fingerprint(key, options.fingerprint_hash,
|
|
|
|
SSH_FP_DEFAULT);
|
|
|
|
debug3_f("key %d: %s %s", i, sshkey_ssh_name(key), fp);
|
|
|
|
free(fp);
|
|
|
|
if (nkeys == 0) {
|
|
|
|
/*
|
|
|
|
* Start building the request when we find the
|
|
|
|
* first usable key.
|
|
|
|
*/
|
|
|
|
if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
|
|
|
|
(r = sshpkt_put_cstring(ssh, "hostkeys-00@openssh.com")) != 0 ||
|
|
|
|
(r = sshpkt_put_u8(ssh, 0)) != 0) /* want reply */
|
|
|
|
sshpkt_fatal(ssh, r, "%s: start request", __func__);
|
|
|
|
}
|
|
|
|
/* Append the key to the request */
|
|
|
|
sshbuf_reset(buf);
|
|
|
|
if ((r = sshkey_putb(key, buf)) != 0)
|
|
|
|
fatal_fr(r, "couldn't put hostkey %d", i);
|
|
|
|
if ((r = sshpkt_put_stringb(ssh, buf)) != 0)
|
|
|
|
sshpkt_fatal(ssh, r, "%s: append key", __func__);
|
|
|
|
nkeys++;
|
|
|
|
}
|
|
|
|
debug3_f("sent %u hostkeys", nkeys);
|
|
|
|
if (nkeys == 0)
|
|
|
|
fatal_f("no hostkeys");
|
|
|
|
if ((r = sshpkt_send(ssh)) != 0)
|
|
|
|
sshpkt_fatal(ssh, r, "%s: send", __func__);
|
|
|
|
sshbuf_free(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
usage(void)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s, %s\n", SSH_RELEASE, SSH_OPENSSL_VERSION);
|
|
|
|
fprintf(stderr,
|
|
|
|
"usage: sshd [-46DdeGiqTtV] [-C connection_spec] [-c host_cert_file]\n"
|
|
|
|
" [-E log_file] [-f config_file] [-g login_grace_time]\n"
|
|
|
|
" [-h host_key_file] [-o option] [-p port] [-u len]\n"
|
|
|
|
);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_hostkeys(struct sshbuf *hostkeys)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
u_int num_keys = 0;
|
|
|
|
struct sshkey *k;
|
|
|
|
struct sshbuf *kbuf;
|
|
|
|
const u_char *cp;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
while (sshbuf_len(hostkeys) != 0) {
|
|
|
|
if (num_keys > 2048)
|
|
|
|
fatal_f("too many hostkeys");
|
|
|
|
sensitive_data.host_keys = xrecallocarray(
|
|
|
|
sensitive_data.host_keys, num_keys, num_keys + 1,
|
|
|
|
sizeof(*sensitive_data.host_pubkeys));
|
|
|
|
sensitive_data.host_pubkeys = xrecallocarray(
|
|
|
|
sensitive_data.host_pubkeys, num_keys, num_keys + 1,
|
|
|
|
sizeof(*sensitive_data.host_pubkeys));
|
|
|
|
sensitive_data.host_certificates = xrecallocarray(
|
|
|
|
sensitive_data.host_certificates, num_keys, num_keys + 1,
|
|
|
|
sizeof(*sensitive_data.host_certificates));
|
|
|
|
/* private key */
|
|
|
|
k = NULL;
|
|
|
|
if ((r = sshbuf_froms(hostkeys, &kbuf)) != 0)
|
|
|
|
fatal_fr(r, "extract privkey");
|
|
|
|
if (sshbuf_len(kbuf) != 0 &&
|
|
|
|
(r = sshkey_private_deserialize(kbuf, &k)) != 0)
|
|
|
|
fatal_fr(r, "parse pubkey");
|
|
|
|
sensitive_data.host_keys[num_keys] = k;
|
|
|
|
sshbuf_free(kbuf);
|
|
|
|
if (k)
|
|
|
|
debug2_f("privkey %u: %s", num_keys, sshkey_ssh_name(k));
|
|
|
|
/* public key */
|
|
|
|
k = NULL;
|
|
|
|
if ((r = sshbuf_get_string_direct(hostkeys, &cp, &len)) != 0)
|
|
|
|
fatal_fr(r, "extract pubkey");
|
|
|
|
if (len != 0 && (r = sshkey_from_blob(cp, len, &k)) != 0)
|
|
|
|
fatal_fr(r, "parse pubkey");
|
|
|
|
sensitive_data.host_pubkeys[num_keys] = k;
|
|
|
|
if (k)
|
|
|
|
debug2_f("pubkey %u: %s", num_keys, sshkey_ssh_name(k));
|
|
|
|
/* certificate */
|
|
|
|
k = NULL;
|
|
|
|
if ((r = sshbuf_get_string_direct(hostkeys, &cp, &len)) != 0)
|
|
|
|
fatal_fr(r, "extract pubkey");
|
|
|
|
if (len != 0 && (r = sshkey_from_blob(cp, len, &k)) != 0)
|
|
|
|
fatal_fr(r, "parse pubkey");
|
|
|
|
sensitive_data.host_certificates[num_keys] = k;
|
|
|
|
if (k)
|
|
|
|
debug2_f("cert %u: %s", num_keys, sshkey_ssh_name(k));
|
|
|
|
num_keys++;
|
|
|
|
}
|
|
|
|
sensitive_data.num_hostkeys = num_keys;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
recv_rexec_state(int fd, struct sshbuf *conf, uint64_t *timing_secretp)
|
|
|
|
{
|
|
|
|
struct sshbuf *m, *inc, *hostkeys;
|
|
|
|
u_char *cp, ver;
|
|
|
|
size_t len;
|
|
|
|
int r;
|
|
|
|
struct include_item *item;
|
|
|
|
|
|
|
|
debug3_f("entering fd = %d", fd);
|
|
|
|
|
|
|
|
if ((m = sshbuf_new()) == NULL || (inc = sshbuf_new()) == NULL)
|
|
|
|
fatal_f("sshbuf_new failed");
|
|
|
|
if (ssh_msg_recv(fd, m) == -1)
|
|
|
|
fatal_f("ssh_msg_recv failed");
|
|
|
|
if ((r = sshbuf_get_u8(m, &ver)) != 0)
|
|
|
|
fatal_fr(r, "parse version");
|
|
|
|
if (ver != 0)
|
|
|
|
fatal_f("rexec version mismatch");
|
|
|
|
if ((r = sshbuf_get_string(m, &cp, &len)) != 0 || /* XXX _direct */
|
|
|
|
(r = sshbuf_get_u64(m, timing_secretp)) != 0 ||
|
|
|
|
(r = sshbuf_froms(m, &hostkeys)) != 0 ||
|
|
|
|
(r = sshbuf_get_stringb(m, inc)) != 0)
|
|
|
|
fatal_fr(r, "parse config");
|
|
|
|
|
|
|
|
if (conf != NULL && (r = sshbuf_put(conf, cp, len)))
|
|
|
|
fatal_fr(r, "sshbuf_put");
|
|
|
|
|
|
|
|
while (sshbuf_len(inc) != 0) {
|
|
|
|
item = xcalloc(1, sizeof(*item));
|
|
|
|
if ((item->contents = sshbuf_new()) == NULL)
|
|
|
|
fatal_f("sshbuf_new failed");
|
|
|
|
if ((r = sshbuf_get_cstring(inc, &item->selector, NULL)) != 0 ||
|
|
|
|
(r = sshbuf_get_cstring(inc, &item->filename, NULL)) != 0 ||
|
|
|
|
(r = sshbuf_get_stringb(inc, item->contents)) != 0)
|
|
|
|
fatal_fr(r, "parse includes");
|
|
|
|
TAILQ_INSERT_TAIL(&includes, item, entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
parse_hostkeys(hostkeys);
|
|
|
|
|
|
|
|
free(cp);
|
|
|
|
sshbuf_free(m);
|
|
|
|
sshbuf_free(hostkeys);
|
|
|
|
sshbuf_free(inc);
|
|
|
|
|
|
|
|
debug3_f("done");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If IP options are supported, make sure there are none (log and
|
|
|
|
* return an error if any are found). Basically we are worried about
|
|
|
|
* source routing; it can be used to pretend you are somebody
|
|
|
|
* (ip-address) you are not. That itself may be "almost acceptable"
|
|
|
|
* under certain circumstances, but rhosts authentication is useless
|
|
|
|
* if source routing is accepted. Notice also that if we just dropped
|
|
|
|
* source routing here, the other side could use IP spoofing to do
|
|
|
|
* rest of the interaction and could still bypass security. So we
|
|
|
|
* exit here if we detect any IP options.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
check_ip_options(struct ssh *ssh)
|
|
|
|
{
|
|
|
|
#ifdef IP_OPTIONS
|
|
|
|
int sock_in = ssh_packet_get_connection_in(ssh);
|
|
|
|
struct sockaddr_storage from;
|
|
|
|
u_char opts[200];
|
|
|
|
socklen_t i, option_size = sizeof(opts), fromlen = sizeof(from);
|
|
|
|
char text[sizeof(opts) * 3 + 1];
|
|
|
|
|
|
|
|
memset(&from, 0, sizeof(from));
|
|
|
|
if (getpeername(sock_in, (struct sockaddr *)&from,
|
|
|
|
&fromlen) == -1)
|
|
|
|
return;
|
|
|
|
if (from.ss_family != AF_INET)
|
|
|
|
return;
|
|
|
|
/* XXX IPv6 options? */
|
|
|
|
|
|
|
|
if (getsockopt(sock_in, IPPROTO_IP, IP_OPTIONS, opts,
|
|
|
|
&option_size) >= 0 && option_size != 0) {
|
|
|
|
text[0] = '\0';
|
|
|
|
for (i = 0; i < option_size; i++)
|
|
|
|
snprintf(text + i*3, sizeof(text) - i*3,
|
|
|
|
" %2.2x", opts[i]);
|
|
|
|
fatal("Connection from %.100s port %d with IP opts: %.800s",
|
|
|
|
ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), text);
|
|
|
|
}
|
|
|
|
#endif /* IP_OPTIONS */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set the routing domain for this process */
|
|
|
|
static void
|
|
|
|
set_process_rdomain(struct ssh *ssh, const char *name)
|
|
|
|
{
|
|
|
|
#if defined(HAVE_SYS_SET_PROCESS_RDOMAIN)
|
|
|
|
if (name == NULL)
|
|
|
|
return; /* default */
|
|
|
|
|
|
|
|
if (strcmp(name, "%D") == 0) {
|
|
|
|
/* "expands" to routing domain of connection */
|
|
|
|
if ((name = ssh_packet_rdomain_in(ssh)) == NULL)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* NB. We don't pass 'ssh' to sys_set_process_rdomain() */
|
|
|
|
return sys_set_process_rdomain(name);
|
|
|
|
#elif defined(__OpenBSD__)
|
|
|
|
int rtable, ortable = getrtable();
|
|
|
|
const char *errstr;
|
|
|
|
|
|
|
|
if (name == NULL)
|
|
|
|
return; /* default */
|
|
|
|
|
|
|
|
if (strcmp(name, "%D") == 0) {
|
|
|
|
/* "expands" to routing domain of connection */
|
|
|
|
if ((name = ssh_packet_rdomain_in(ssh)) == NULL)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rtable = (int)strtonum(name, 0, 255, &errstr);
|
|
|
|
if (errstr != NULL) /* Shouldn't happen */
|
|
|
|
fatal("Invalid routing domain \"%s\": %s", name, errstr);
|
|
|
|
if (rtable != ortable && setrtable(rtable) != 0)
|
|
|
|
fatal("Unable to set routing domain %d: %s",
|
|
|
|
rtable, strerror(errno));
|
|
|
|
debug_f("set routing domain %d (was %d)", rtable, ortable);
|
|
|
|
#else /* defined(__OpenBSD__) */
|
|
|
|
fatal("Unable to set routing domain: not supported in this platform");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Main program for the daemon.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
main(int ac, char **av)
|
|
|
|
{
|
|
|
|
struct ssh *ssh = NULL;
|
|
|
|
extern char *optarg;
|
|
|
|
extern int optind;
|
2024-10-14 01:57:50 +00:00
|
|
|
int devnull, r, opt, on = 1, remote_port;
|
2024-05-17 00:30:23 +00:00
|
|
|
int sock_in = -1, sock_out = -1, rexeced_flag = 0, have_key = 0;
|
|
|
|
const char *remote_ip, *rdomain;
|
|
|
|
char *line, *laddr, *logfile = NULL;
|
|
|
|
u_int i;
|
|
|
|
u_int64_t ibytes, obytes;
|
|
|
|
mode_t new_umask;
|
|
|
|
Authctxt *authctxt;
|
|
|
|
struct connection_info *connection_info = NULL;
|
|
|
|
sigset_t sigmask;
|
|
|
|
uint64_t timing_secret = 0;
|
2024-07-31 12:00:18 +00:00
|
|
|
struct itimerval itv;
|
2024-05-17 00:30:23 +00:00
|
|
|
|
|
|
|
sigemptyset(&sigmask);
|
|
|
|
sigprocmask(SIG_SETMASK, &sigmask, NULL);
|
|
|
|
|
|
|
|
#ifdef HAVE_SECUREWARE
|
|
|
|
(void)set_auth_parameters(ac, av);
|
|
|
|
#endif
|
|
|
|
__progname = ssh_get_progname(av[0]);
|
|
|
|
|
|
|
|
/* Save argv. Duplicate so setproctitle emulation doesn't clobber it */
|
|
|
|
saved_argc = ac;
|
|
|
|
saved_argv = xcalloc(ac + 1, sizeof(*saved_argv));
|
|
|
|
for (i = 0; (int)i < ac; i++)
|
|
|
|
saved_argv[i] = xstrdup(av[i]);
|
|
|
|
saved_argv[i] = NULL;
|
|
|
|
|
|
|
|
#ifndef HAVE_SETPROCTITLE
|
|
|
|
/* Prepare for later setproctitle emulation */
|
|
|
|
compat_init_setproctitle(ac, av);
|
|
|
|
av = saved_argv;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
|
|
|
|
sanitise_stdfd();
|
|
|
|
|
|
|
|
/* Initialize configuration options to their default values. */
|
|
|
|
initialize_server_options(&options);
|
|
|
|
|
|
|
|
/* Parse command-line arguments. */
|
|
|
|
while ((opt = getopt(ac, av,
|
|
|
|
"C:E:b:c:f:g:h:k:o:p:u:46DGQRTdeiqrtV")) != -1) {
|
|
|
|
switch (opt) {
|
|
|
|
case '4':
|
|
|
|
options.address_family = AF_INET;
|
|
|
|
break;
|
|
|
|
case '6':
|
|
|
|
options.address_family = AF_INET6;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
config_file_name = optarg;
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
servconf_add_hostcert("[command-line]", 0,
|
|
|
|
&options, optarg);
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
if (debug_flag == 0) {
|
|
|
|
debug_flag = 1;
|
|
|
|
options.log_level = SYSLOG_LEVEL_DEBUG1;
|
|
|
|
} else if (options.log_level < SYSLOG_LEVEL_DEBUG3)
|
|
|
|
options.log_level++;
|
|
|
|
break;
|
|
|
|
case 'D':
|
|
|
|
/* ignore */
|
|
|
|
break;
|
|
|
|
case 'E':
|
|
|
|
logfile = optarg;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case 'e':
|
|
|
|
log_stderr = 1;
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
inetd_flag = 1;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
/* ignore */
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
rexeced_flag = 1;
|
|
|
|
break;
|
|
|
|
case 'Q':
|
|
|
|
/* ignored */
|
|
|
|
break;
|
|
|
|
case 'q':
|
|
|
|
options.log_level = SYSLOG_LEVEL_QUIET;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
/* protocol 1, ignored */
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
options.ports_from_cmdline = 1;
|
|
|
|
if (options.num_ports >= MAX_PORTS) {
|
|
|
|
fprintf(stderr, "too many ports.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
options.ports[options.num_ports++] = a2port(optarg);
|
|
|
|
if (options.ports[options.num_ports-1] <= 0) {
|
|
|
|
fprintf(stderr, "Bad port number.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'g':
|
|
|
|
if ((options.login_grace_time = convtime(optarg)) == -1) {
|
|
|
|
fprintf(stderr, "Invalid login grace time.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'k':
|
|
|
|
/* protocol 1, ignored */
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
servconf_add_hostkey("[command-line]", 0,
|
|
|
|
&options, optarg, 1);
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
case 'T':
|
|
|
|
case 'G':
|
|
|
|
fatal("test/dump modes not supported");
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
connection_info = server_get_connection_info(ssh, 0, 0);
|
|
|
|
if (parse_server_match_testspec(connection_info,
|
|
|
|
optarg) == -1)
|
|
|
|
exit(1);
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
utmp_len = (u_int)strtonum(optarg, 0, HOST_NAME_MAX+1+1, NULL);
|
|
|
|
if (utmp_len > HOST_NAME_MAX+1) {
|
|
|
|
fprintf(stderr, "Invalid utmp length.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
line = xstrdup(optarg);
|
|
|
|
if (process_server_config_line(&options, line,
|
|
|
|
"command-line", 0, NULL, NULL, &includes) != 0)
|
|
|
|
exit(1);
|
|
|
|
free(line);
|
|
|
|
break;
|
|
|
|
case 'V':
|
|
|
|
fprintf(stderr, "%s, %s\n",
|
|
|
|
SSH_RELEASE, SSH_OPENSSL_VERSION);
|
|
|
|
exit(0);
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that there are no remaining arguments. */
|
|
|
|
if (optind < ac) {
|
|
|
|
fprintf(stderr, "Extra argument %s.\n", av[optind]);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
debug("sshd version %s, %s", SSH_VERSION, SSH_OPENSSL_VERSION);
|
|
|
|
|
|
|
|
if (!rexeced_flag)
|
|
|
|
fatal("sshd-session should not be executed directly");
|
|
|
|
|
|
|
|
closefrom(REEXEC_MIN_FREE_FD);
|
|
|
|
|
2024-10-14 01:57:50 +00:00
|
|
|
/* Reserve fds we'll need later for reexec things */
|
|
|
|
if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1)
|
|
|
|
fatal("open %s: %s", _PATH_DEVNULL, strerror(errno));
|
|
|
|
while (devnull < PRIVSEP_MIN_FREE_FD) {
|
|
|
|
if ((devnull = dup(devnull)) == -1)
|
|
|
|
fatal("dup %s: %s", _PATH_DEVNULL, strerror(errno));
|
|
|
|
}
|
|
|
|
|
2024-05-17 00:30:23 +00:00
|
|
|
seed_rng();
|
|
|
|
|
|
|
|
/* If requested, redirect the logs to the specified logfile. */
|
|
|
|
if (logfile != NULL) {
|
|
|
|
char *cp, pid_s[32];
|
|
|
|
|
|
|
|
snprintf(pid_s, sizeof(pid_s), "%ld", (unsigned long)getpid());
|
|
|
|
cp = percent_expand(logfile,
|
|
|
|
"p", pid_s,
|
|
|
|
"P", "sshd-session",
|
|
|
|
(char *)NULL);
|
|
|
|
log_redirect_stderr_to(cp);
|
|
|
|
free(cp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Force logging to stderr until we have loaded the private host
|
|
|
|
* key (unless started from inetd)
|
|
|
|
*/
|
|
|
|
log_init(__progname,
|
|
|
|
options.log_level == SYSLOG_LEVEL_NOT_SET ?
|
|
|
|
SYSLOG_LEVEL_INFO : options.log_level,
|
|
|
|
options.log_facility == SYSLOG_FACILITY_NOT_SET ?
|
|
|
|
SYSLOG_FACILITY_AUTH : options.log_facility,
|
|
|
|
log_stderr || !inetd_flag || debug_flag);
|
|
|
|
|
|
|
|
debug("sshd version %s, %s", SSH_VERSION, SSH_OPENSSL_VERSION);
|
|
|
|
|
2024-06-13 01:33:09 +00:00
|
|
|
/* Fetch our configuration */
|
|
|
|
if ((cfg = sshbuf_new()) == NULL)
|
|
|
|
fatal("sshbuf_new config buf failed");
|
|
|
|
setproctitle("%s", "[rexeced]");
|
|
|
|
recv_rexec_state(REEXEC_CONFIG_PASS_FD, cfg, &timing_secret);
|
2024-10-14 01:57:50 +00:00
|
|
|
/* close the fd, but keep the slot reserved */
|
|
|
|
if (dup2(devnull, REEXEC_CONFIG_PASS_FD) == -1)
|
|
|
|
fatal("dup2 devnull->config fd: %s", strerror(errno));
|
2024-06-13 01:33:09 +00:00
|
|
|
parse_server_config(&options, "rexec", cfg, &includes, NULL, 1);
|
|
|
|
/* Fill in default values for those options not explicitly set. */
|
|
|
|
fill_default_server_options(&options);
|
|
|
|
options.timing_secret = timing_secret;
|
|
|
|
|
2024-05-17 00:30:23 +00:00
|
|
|
/* Store privilege separation user for later use if required. */
|
|
|
|
privsep_chroot = (getuid() == 0 || geteuid() == 0);
|
|
|
|
if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) {
|
|
|
|
if (privsep_chroot || options.kerberos_authentication)
|
|
|
|
fatal("Privilege separation user %s does not exist",
|
|
|
|
SSH_PRIVSEP_USER);
|
|
|
|
} else {
|
|
|
|
privsep_pw = pwcopy(privsep_pw);
|
|
|
|
freezero(privsep_pw->pw_passwd, strlen(privsep_pw->pw_passwd));
|
|
|
|
privsep_pw->pw_passwd = xstrdup("*");
|
|
|
|
}
|
|
|
|
endpwent();
|
|
|
|
|
2024-10-14 01:57:50 +00:00
|
|
|
if (!debug_flag && !inetd_flag) {
|
|
|
|
if ((startup_pipe = dup(REEXEC_STARTUP_PIPE_FD)) == -1)
|
|
|
|
fatal("internal error: no startup pipe");
|
|
|
|
/* close the fd, but keep the slot reserved */
|
|
|
|
if (dup2(devnull, REEXEC_STARTUP_PIPE_FD) == -1)
|
|
|
|
fatal("dup2 devnull->startup fd: %s", strerror(errno));
|
|
|
|
|
2024-05-17 00:30:23 +00:00
|
|
|
/*
|
|
|
|
* Signal parent that this child is at a point where
|
|
|
|
* they can go away if they have a SIGHUP pending.
|
|
|
|
*/
|
|
|
|
(void)atomicio(vwrite, startup_pipe, "\0", 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that options are sensible */
|
|
|
|
if (options.authorized_keys_command_user == NULL &&
|
|
|
|
(options.authorized_keys_command != NULL &&
|
|
|
|
strcasecmp(options.authorized_keys_command, "none") != 0))
|
|
|
|
fatal("AuthorizedKeysCommand set without "
|
|
|
|
"AuthorizedKeysCommandUser");
|
|
|
|
if (options.authorized_principals_command_user == NULL &&
|
|
|
|
(options.authorized_principals_command != NULL &&
|
|
|
|
strcasecmp(options.authorized_principals_command, "none") != 0))
|
|
|
|
fatal("AuthorizedPrincipalsCommand set without "
|
|
|
|
"AuthorizedPrincipalsCommandUser");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check whether there is any path through configured auth methods.
|
|
|
|
* Unfortunately it is not possible to verify this generally before
|
|
|
|
* daemonisation in the presence of Match block, but this catches
|
|
|
|
* and warns for trivial misconfigurations that could break login.
|
|
|
|
*/
|
|
|
|
if (options.num_auth_methods != 0) {
|
|
|
|
for (i = 0; i < options.num_auth_methods; i++) {
|
|
|
|
if (auth2_methods_valid(options.auth_methods[i],
|
|
|
|
1) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i >= options.num_auth_methods)
|
|
|
|
fatal("AuthenticationMethods cannot be satisfied by "
|
|
|
|
"enabled authentication methods");
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef WITH_OPENSSL
|
|
|
|
if (options.moduli_file != NULL)
|
|
|
|
dh_set_moduli_file(options.moduli_file);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (options.host_key_agent) {
|
|
|
|
if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME))
|
|
|
|
setenv(SSH_AUTHSOCKET_ENV_NAME,
|
|
|
|
options.host_key_agent, 1);
|
|
|
|
if ((r = ssh_get_authentication_socket(NULL)) == 0)
|
|
|
|
have_agent = 1;
|
|
|
|
else
|
|
|
|
error_r(r, "Could not connect to agent \"%s\"",
|
|
|
|
options.host_key_agent);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.num_host_key_files != sensitive_data.num_hostkeys) {
|
|
|
|
fatal("internal error: hostkeys confused (config %u recvd %u)",
|
|
|
|
options.num_host_key_files, sensitive_data.num_hostkeys);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < options.num_host_key_files; i++) {
|
|
|
|
if (sensitive_data.host_keys[i] != NULL ||
|
|
|
|
(have_agent && sensitive_data.host_pubkeys[i] != NULL)) {
|
|
|
|
have_key = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!have_key)
|
2024-05-17 02:39:11 +00:00
|
|
|
fatal("internal error: monitor received no hostkeys");
|
2024-05-17 00:30:23 +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). */
|
|
|
|
if (debug_flag)
|
|
|
|
log_stderr = 1;
|
|
|
|
log_init(__progname, options.log_level,
|
|
|
|
options.log_facility, log_stderr);
|
|
|
|
for (i = 0; i < options.num_log_verbose; i++)
|
|
|
|
log_verbose_add(options.log_verbose[i]);
|
|
|
|
|
|
|
|
/* Reinitialize the log (because of the fork above). */
|
|
|
|
log_init(__progname, options.log_level, options.log_facility, log_stderr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Chdir to the root directory so that the current disk can be
|
|
|
|
* unmounted if desired.
|
|
|
|
*/
|
|
|
|
if (chdir("/") == -1)
|
|
|
|
error("chdir(\"/\"): %s", strerror(errno));
|
|
|
|
|
|
|
|
/* ignore SIGPIPE */
|
|
|
|
ssh_signal(SIGPIPE, SIG_IGN);
|
|
|
|
|
|
|
|
/* Get a connection, either from inetd or rexec */
|
|
|
|
if (inetd_flag) {
|
|
|
|
/*
|
|
|
|
* NB. must be different fd numbers for the !socket case,
|
|
|
|
* as packet_connection_is_on_socket() depends on this.
|
|
|
|
*/
|
|
|
|
sock_in = dup(STDIN_FILENO);
|
|
|
|
sock_out = dup(STDOUT_FILENO);
|
|
|
|
} else {
|
|
|
|
/* rexec case; accept()ed socket in ancestor listener */
|
|
|
|
sock_in = sock_out = dup(STDIN_FILENO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We intentionally do not close the descriptors 0, 1, and 2
|
|
|
|
* as our code for setting the descriptors won't work if
|
|
|
|
* ttyfd happens to be one of those.
|
|
|
|
*/
|
|
|
|
if (stdfd_devnull(1, 1, !log_stderr) == -1)
|
|
|
|
error("stdfd_devnull failed");
|
|
|
|
debug("network sockets: %d, %d", sock_in, sock_out);
|
|
|
|
|
|
|
|
/* This is the child processing a new connection. */
|
|
|
|
setproctitle("%s", "[accepted]");
|
|
|
|
|
|
|
|
/* Executed child processes don't need these. */
|
|
|
|
fcntl(sock_out, F_SETFD, FD_CLOEXEC);
|
|
|
|
fcntl(sock_in, F_SETFD, FD_CLOEXEC);
|
|
|
|
|
|
|
|
/* We will not restart on SIGHUP since it no longer makes sense. */
|
|
|
|
ssh_signal(SIGALRM, SIG_DFL);
|
|
|
|
ssh_signal(SIGHUP, SIG_DFL);
|
|
|
|
ssh_signal(SIGTERM, SIG_DFL);
|
|
|
|
ssh_signal(SIGQUIT, SIG_DFL);
|
|
|
|
ssh_signal(SIGCHLD, SIG_DFL);
|
|
|
|
ssh_signal(SIGINT, SIG_DFL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Register our connection. This turns encryption off because we do
|
|
|
|
* not have a key.
|
|
|
|
*/
|
|
|
|
if ((ssh = ssh_packet_set_connection(NULL, sock_in, sock_out)) == NULL)
|
|
|
|
fatal("Unable to create connection");
|
|
|
|
the_active_state = ssh;
|
|
|
|
ssh_packet_set_server(ssh);
|
|
|
|
|
|
|
|
check_ip_options(ssh);
|
|
|
|
|
|
|
|
/* Prepare the channels layer */
|
|
|
|
channel_init_channels(ssh);
|
|
|
|
channel_set_af(ssh, options.address_family);
|
|
|
|
server_process_channel_timeouts(ssh);
|
|
|
|
server_process_permitopen(ssh);
|
|
|
|
|
|
|
|
/* Set SO_KEEPALIVE if requested. */
|
|
|
|
if (options.tcp_keep_alive && ssh_packet_connection_is_on_socket(ssh) &&
|
|
|
|
setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1)
|
|
|
|
error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
|
|
|
|
|
|
|
|
if ((remote_port = ssh_remote_port(ssh)) < 0) {
|
|
|
|
debug("ssh_remote_port failed");
|
|
|
|
cleanup_exit(255);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The rest of the code depends on the fact that
|
|
|
|
* ssh_remote_ipaddr() caches the remote ip, even if
|
|
|
|
* the socket goes away.
|
|
|
|
*/
|
|
|
|
remote_ip = ssh_remote_ipaddr(ssh);
|
|
|
|
|
|
|
|
#ifdef SSH_AUDIT_EVENTS
|
|
|
|
audit_connection_from(remote_ip, remote_port);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
rdomain = ssh_packet_rdomain_in(ssh);
|
|
|
|
|
|
|
|
/* Log the connection. */
|
|
|
|
laddr = get_local_ipaddr(sock_in);
|
|
|
|
verbose("Connection from %s port %d on %s port %d%s%s%s",
|
|
|
|
remote_ip, remote_port, laddr, ssh_local_port(ssh),
|
|
|
|
rdomain == NULL ? "" : " rdomain \"",
|
|
|
|
rdomain == NULL ? "" : rdomain,
|
|
|
|
rdomain == NULL ? "" : "\"");
|
|
|
|
free(laddr);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We don't want to listen forever unless the other side
|
|
|
|
* successfully authenticates itself. So we set up an alarm which is
|
|
|
|
* cleared after successful authentication. A limit of zero
|
|
|
|
* indicates no limit. Note that we don't set the alarm in debugging
|
|
|
|
* mode; it is just annoying to have the server exit just when you
|
|
|
|
* are about to discover the bug.
|
|
|
|
*/
|
|
|
|
ssh_signal(SIGALRM, grace_alarm_handler);
|
2024-07-31 12:00:18 +00:00
|
|
|
if (!debug_flag && options.login_grace_time > 0) {
|
|
|
|
int ujitter = arc4random_uniform(4 * 1000000);
|
|
|
|
|
|
|
|
timerclear(&itv.it_interval);
|
|
|
|
itv.it_value.tv_sec = options.login_grace_time;
|
|
|
|
itv.it_value.tv_sec += ujitter / 1000000;
|
|
|
|
itv.it_value.tv_usec = ujitter % 1000000;
|
|
|
|
|
|
|
|
if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
|
|
|
|
fatal("login grace time setitimer failed");
|
|
|
|
}
|
2024-05-17 00:30:23 +00:00
|
|
|
|
|
|
|
if ((r = kex_exchange_identification(ssh, -1,
|
|
|
|
options.version_addendum)) != 0)
|
|
|
|
sshpkt_fatal(ssh, r, "banner exchange");
|
|
|
|
|
|
|
|
ssh_packet_set_nonblocking(ssh);
|
|
|
|
|
|
|
|
/* allocate authentication context */
|
|
|
|
authctxt = xcalloc(1, sizeof(*authctxt));
|
|
|
|
ssh->authctxt = authctxt;
|
|
|
|
|
|
|
|
/* XXX global for cleanup, access from other modules */
|
|
|
|
the_authctxt = authctxt;
|
|
|
|
|
|
|
|
/* Set default key authentication options */
|
|
|
|
if ((auth_opts = sshauthopt_new_with_keys_defaults()) == NULL)
|
|
|
|
fatal("allocation failed");
|
|
|
|
|
|
|
|
/* prepare buffer to collect messages to display to user after login */
|
|
|
|
if ((loginmsg = sshbuf_new()) == NULL)
|
|
|
|
fatal("sshbuf_new loginmsg failed");
|
|
|
|
auth_debug_reset();
|
|
|
|
|
2024-10-14 01:57:50 +00:00
|
|
|
if (privsep_preauth(ssh) != 1)
|
|
|
|
fatal("privsep_preauth failed");
|
2024-05-17 00:30:23 +00:00
|
|
|
|
2024-10-14 01:57:50 +00:00
|
|
|
/* Now user is authenticated */
|
2024-05-17 00:30:23 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Cancel the alarm we set to limit the time taken for
|
|
|
|
* authentication.
|
|
|
|
*/
|
2024-07-31 12:00:18 +00:00
|
|
|
timerclear(&itv.it_interval);
|
|
|
|
timerclear(&itv.it_value);
|
|
|
|
if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
|
|
|
|
fatal("login grace time clear failed");
|
2024-05-17 00:30:23 +00:00
|
|
|
ssh_signal(SIGALRM, SIG_DFL);
|
|
|
|
authctxt->authenticated = 1;
|
|
|
|
if (startup_pipe != -1) {
|
upstream: Add a facility to sshd(8) to penalise particular
problematic client behaviours, controlled by two new sshd_config(5) options:
PerSourcePenalties and PerSourcePenaltyExemptList.
When PerSourcePenalties are enabled, sshd(8) will monitor the exit
status of its child pre-auth session processes. Through the exit
status, it can observe situations where the session did not
authenticate as expected. These conditions include when the client
repeatedly attempted authentication unsucessfully (possibly indicating
an attack against one or more accounts, e.g. password guessing), or
when client behaviour caused sshd to crash (possibly indicating
attempts to exploit sshd).
When such a condition is observed, sshd will record a penalty of some
duration (e.g. 30 seconds) against the client's address. If this time
is above a minimum threshold specified by the PerSourcePenalties, then
connections from the client address will be refused (along with any
others in the same PerSourceNetBlockSize CIDR range).
Repeated offenses by the same client address will accrue greater
penalties, up to a configurable maximum. A PerSourcePenaltyExemptList
option allows certain address ranges to be exempt from all penalties.
We hope these options will make it significantly more difficult for
attackers to find accounts with weak/guessable passwords or exploit
bugs in sshd(8) itself.
PerSourcePenalties is off by default, but we expect to enable it
automatically in the near future.
much feedback markus@ and others, ok markus@
OpenBSD-Commit-ID: 89ded70eccb2b4926ef0366a4d58a693de366cca
2024-06-06 17:15:25 +00:00
|
|
|
/* signal listener that authentication completed successfully */
|
|
|
|
(void)atomicio(vwrite, startup_pipe, "\001", 1);
|
2024-05-17 00:30:23 +00:00
|
|
|
close(startup_pipe);
|
|
|
|
startup_pipe = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.routing_domain != NULL)
|
|
|
|
set_process_rdomain(ssh, options.routing_domain);
|
|
|
|
|
|
|
|
#ifdef SSH_AUDIT_EVENTS
|
|
|
|
audit_event(ssh, SSH_AUTH_SUCCESS);
|
|
|
|
#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();
|
|
|
|
do_pam_session(ssh);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In privilege separation, we fork another child and prepare
|
|
|
|
* file descriptor passing.
|
|
|
|
*/
|
|
|
|
privsep_postauth(ssh, authctxt);
|
|
|
|
/* the monitor process [priv] will not return */
|
|
|
|
|
|
|
|
ssh_packet_set_timeout(ssh, options.client_alive_interval,
|
|
|
|
options.client_alive_count_max);
|
|
|
|
|
|
|
|
/* Try to send all our hostkeys to the client */
|
|
|
|
notify_hostkeys(ssh);
|
|
|
|
|
|
|
|
/* Start session. */
|
|
|
|
do_authenticated(ssh, authctxt);
|
|
|
|
|
|
|
|
/* The connection has been terminated. */
|
|
|
|
ssh_packet_get_bytes(ssh, &ibytes, &obytes);
|
|
|
|
verbose("Transferred: sent %llu, received %llu bytes",
|
|
|
|
(unsigned long long)obytes, (unsigned long long)ibytes);
|
|
|
|
|
|
|
|
verbose("Closing connection to %.500s port %d", remote_ip, remote_port);
|
|
|
|
|
|
|
|
#ifdef USE_PAM
|
|
|
|
if (options.use_pam)
|
|
|
|
finish_pam();
|
|
|
|
#endif /* USE_PAM */
|
|
|
|
|
|
|
|
#ifdef SSH_AUDIT_EVENTS
|
|
|
|
mm_audit_event(ssh, SSH_CONNECTION_CLOSE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ssh_packet_close(ssh);
|
|
|
|
|
|
|
|
mm_terminate();
|
|
|
|
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
sshd_hostkey_sign(struct ssh *ssh, struct sshkey *privkey,
|
|
|
|
struct sshkey *pubkey, u_char **signature, size_t *slenp,
|
|
|
|
const u_char *data, size_t dlen, const char *alg)
|
|
|
|
{
|
|
|
|
if (privkey) {
|
|
|
|
if (mm_sshkey_sign(ssh, privkey, signature, slenp,
|
|
|
|
data, dlen, alg, options.sk_provider, NULL,
|
|
|
|
ssh->compat) < 0)
|
|
|
|
fatal_f("privkey sign failed");
|
|
|
|
} else {
|
|
|
|
if (mm_sshkey_sign(ssh, pubkey, signature, slenp,
|
|
|
|
data, dlen, alg, options.sk_provider, NULL,
|
|
|
|
ssh->compat) < 0)
|
|
|
|
fatal_f("pubkey sign failed");
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* server specific fatal cleanup */
|
|
|
|
void
|
|
|
|
cleanup_exit(int i)
|
|
|
|
{
|
upstream: Add a facility to sshd(8) to penalise particular
problematic client behaviours, controlled by two new sshd_config(5) options:
PerSourcePenalties and PerSourcePenaltyExemptList.
When PerSourcePenalties are enabled, sshd(8) will monitor the exit
status of its child pre-auth session processes. Through the exit
status, it can observe situations where the session did not
authenticate as expected. These conditions include when the client
repeatedly attempted authentication unsucessfully (possibly indicating
an attack against one or more accounts, e.g. password guessing), or
when client behaviour caused sshd to crash (possibly indicating
attempts to exploit sshd).
When such a condition is observed, sshd will record a penalty of some
duration (e.g. 30 seconds) against the client's address. If this time
is above a minimum threshold specified by the PerSourcePenalties, then
connections from the client address will be refused (along with any
others in the same PerSourceNetBlockSize CIDR range).
Repeated offenses by the same client address will accrue greater
penalties, up to a configurable maximum. A PerSourcePenaltyExemptList
option allows certain address ranges to be exempt from all penalties.
We hope these options will make it significantly more difficult for
attackers to find accounts with weak/guessable passwords or exploit
bugs in sshd(8) itself.
PerSourcePenalties is off by default, but we expect to enable it
automatically in the near future.
much feedback markus@ and others, ok markus@
OpenBSD-Commit-ID: 89ded70eccb2b4926ef0366a4d58a693de366cca
2024-06-06 17:15:25 +00:00
|
|
|
extern int auth_attempted; /* monitor.c */
|
|
|
|
|
2024-05-17 00:30:23 +00:00
|
|
|
if (the_active_state != NULL && the_authctxt != NULL) {
|
|
|
|
do_cleanup(the_active_state, the_authctxt);
|
|
|
|
if (privsep_is_preauth &&
|
|
|
|
pmonitor != NULL && pmonitor->m_pid > 1) {
|
|
|
|
debug("Killing privsep child %d", pmonitor->m_pid);
|
|
|
|
if (kill(pmonitor->m_pid, SIGKILL) != 0 &&
|
|
|
|
errno != ESRCH) {
|
|
|
|
error_f("kill(%d): %s", pmonitor->m_pid,
|
|
|
|
strerror(errno));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef SSH_AUDIT_EVENTS
|
|
|
|
/* done after do_cleanup so it can cancel the PAM auth 'thread' */
|
2024-05-31 09:05:34 +00:00
|
|
|
if (the_active_state != NULL && mm_is_monitor())
|
2024-05-17 00:30:23 +00:00
|
|
|
audit_event(the_active_state, SSH_CONNECTION_ABANDON);
|
|
|
|
#endif
|
2024-08-23 10:21:06 +00:00
|
|
|
/* Override default fatal exit value when auth was attempted */
|
|
|
|
if (i == 255 && auth_attempted)
|
|
|
|
_exit(EXIT_AUTH_ATTEMPTED);
|
2024-05-17 00:30:23 +00:00
|
|
|
_exit(i);
|
|
|
|
}
|