upstream: Add 'invaliduser' penalty to PerSourcePenalties, which is

applied to login attempts for usernames that do not match real accounts.
Defaults to 5s to match 'authfail' but allows administrators to block such
sources for longer if desired.  with & ok djm@

OpenBSD-Commit-ID: bb62797bcf2adceb96f608ce86d0bb042aff5834
This commit is contained in:
dtucker@openbsd.org
2025-12-16 08:32:50 +00:00
committed by Darren Tucker
parent 94bf1154b4
commit 49480f1934
9 changed files with 53 additions and 13 deletions

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: monitor.c,v 1.249 2025/09/25 06:45:50 djm Exp $ */
/* $OpenBSD: monitor.c,v 1.250 2025/12/16 08:32:50 dtucker Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -155,7 +155,8 @@ static char *auth_submethod = NULL;
static u_int session_id2_len = 0;
static u_char *session_id2 = NULL;
static pid_t monitor_child_pid;
int auth_attempted = 0;
static int auth_attempted = 0;
static int invalid_user = 0;
struct mon_table {
enum monitor_reqtype type;
@@ -855,6 +856,7 @@ mm_answer_pwnamallow(struct ssh *ssh, int sock, struct sshbuf *m)
sshbuf_reset(m);
if (pwent == NULL) {
invalid_user = 1;
if ((r = sshbuf_put_u8(m, 0)) != 0)
fatal_fr(r, "assemble fakepw");
authctxt->pw = fakepw();
@@ -1944,6 +1946,18 @@ monitor_reinit(struct monitor *mon)
monitor_openfds(mon, 0);
}
int
monitor_auth_attempted(void)
{
return auth_attempted;
}
int
monitor_invalid_user(void)
{
return invalid_user;
}
#ifdef GSSAPI
int
mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m)

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: monitor.h,v 1.24 2024/05/17 00:30:24 djm Exp $ */
/* $OpenBSD: monitor.h,v 1.26 2025/12/16 08:32:50 dtucker Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
@@ -88,6 +88,9 @@ void monitor_child_postauth(struct ssh *, struct monitor *);
void monitor_clear_keystate(struct ssh *, struct monitor *);
void monitor_apply_keystate(struct ssh *, struct monitor *);
int monitor_auth_attempted(void);
int monitor_invalid_user(void);
/* Prototypes for request sending and receiving */
void mm_request_send(int, enum monitor_reqtype, struct sshbuf *);
void mm_request_receive(int, struct sshbuf *);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: servconf.c,v 1.439 2025/12/08 03:55:22 djm Exp $ */
/* $OpenBSD: servconf.c,v 1.440 2025/12/16 08:32:50 dtucker Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@@ -176,6 +176,7 @@ initialize_server_options(ServerOptions *options)
options->per_source_penalty.overflow_mode6 = -1;
options->per_source_penalty.penalty_crash = -1.0;
options->per_source_penalty.penalty_authfail = -1.0;
options->per_source_penalty.penalty_invaliduser = -1.0;
options->per_source_penalty.penalty_noauth = -1.0;
options->per_source_penalty.penalty_grace = -1.0;
options->per_source_penalty.penalty_refuseconnection = -1.0;
@@ -438,6 +439,8 @@ fill_default_server_options(ServerOptions *options)
options->per_source_penalty.penalty_grace = 10.0;
if (options->per_source_penalty.penalty_authfail < 0.0)
options->per_source_penalty.penalty_authfail = 5.0;
if (options->per_source_penalty.penalty_invaliduser < 0.0)
options->per_source_penalty.penalty_invaliduser = 5.0;
if (options->per_source_penalty.penalty_noauth < 0.0)
options->per_source_penalty.penalty_noauth = 1.0;
if (options->per_source_penalty.penalty_refuseconnection < 0.0)
@@ -2115,6 +2118,8 @@ process_server_config_line_depth(ServerOptions *options, char *line,
doubleptr = &options->per_source_penalty.penalty_crash;
} else if ((q = strprefix(arg, "authfail:", 0)) != NULL) {
doubleptr = &options->per_source_penalty.penalty_authfail;
} else if ((q = strprefix(arg, "invaliduser:", 0)) != NULL) {
doubleptr = &options->per_source_penalty.penalty_invaliduser;
} else if ((q = strprefix(arg, "noauth:", 0)) != NULL) {
doubleptr = &options->per_source_penalty.penalty_noauth;
} else if ((q = strprefix(arg, "grace-exceeded:", 0)) != NULL) {
@@ -3430,12 +3435,14 @@ dump_config(ServerOptions *o)
if (o->per_source_penalty.enabled) {
printf("persourcepenalties crash:%f authfail:%f noauth:%f "
"invaliduser:%f "
"grace-exceeded:%f refuseconnection:%f max:%f min:%f "
"max-sources4:%d max-sources6:%d "
"overflow:%s overflow6:%s\n",
o->per_source_penalty.penalty_crash,
o->per_source_penalty.penalty_authfail,
o->per_source_penalty.penalty_noauth,
o->per_source_penalty.penalty_invaliduser,
o->per_source_penalty.penalty_grace,
o->per_source_penalty.penalty_refuseconnection,
o->per_source_penalty.penalty_max,

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: servconf.h,v 1.171 2025/12/08 03:55:22 djm Exp $ */
/* $OpenBSD: servconf.h,v 1.172 2025/12/16 08:32:50 dtucker Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -76,6 +76,7 @@ struct per_source_penalty {
double penalty_crash;
double penalty_grace;
double penalty_authfail;
double penalty_invaliduser;
double penalty_noauth;
double penalty_refuseconnection;
double penalty_max;

View File

@@ -382,6 +382,10 @@ srclimit_penalise(struct xaddr *addr, int penalty_type)
penalty_secs = penalty_cfg.penalty_noauth;
reason = "penalty: connections without attempting authentication";
break;
case SRCLIMIT_PENALTY_INVALIDUSER:
penalty_secs = penalty_cfg.penalty_invaliduser;
reason = "penalty: attempted authentication by invalid user";
break;
case SRCLIMIT_PENALTY_REFUSECONNECTION:
penalty_secs = penalty_cfg.penalty_refuseconnection;
reason = "penalty: connection prohibited by RefuseConnection";
@@ -434,7 +438,7 @@ srclimit_penalise(struct xaddr *addr, int penalty_type)
fatal_f("internal error: %s penalty tables corrupt", t);
do_log2_f(penalty->active ?
SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE,
"%s: new %s %s penalty of %f seconds for %s", t,
"%s: new %s %s penalty of %.3f seconds for %s", t,
addrnetmask, penalty->active ? "active" : "deferred",
penalty_secs, reason);
if (++(*npenaltiesp) > (size_t)max_sources)

View File

@@ -28,12 +28,14 @@ void srclimit_done(int);
#define SRCLIMIT_PENALTY_GRACE_EXCEEDED 3
#define SRCLIMIT_PENALTY_NOAUTH 4
#define SRCLIMIT_PENALTY_REFUSECONNECTION 5
#define SRCLIMIT_PENALTY_INVALIDUSER 6
/* meaningful exit values, used by sshd listener for penalties */
#define EXIT_LOGIN_GRACE 3 /* login grace period exceeded */
#define EXIT_CHILD_CRASH 4 /* preauth child crashed */
#define EXIT_AUTH_ATTEMPTED 5 /* at least one auth attempt made */
#define EXIT_CONFIG_REFUSED 6 /* sshd_config RefuseConnection */
#define EXIT_INVALID_USER 7 /* invalid user supplied */
void srclimit_penalise(struct xaddr *, int);
int srclimit_penalty_check_allow(int, const char **);

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: sshd-session.c,v 1.17 2025/11/13 10:35:14 dtucker Exp $ */
/* $OpenBSD: sshd-session.c,v 1.18 2025/12/16 08:32:50 dtucker Exp $ */
/*
* SSH2 implementation:
* Privilege Separation:
@@ -1375,8 +1375,6 @@ sshd_hostkey_sign(struct ssh *ssh, struct sshkey *privkey,
void
cleanup_exit(int i)
{
extern int auth_attempted; /* monitor.c */
if (the_active_state != NULL && the_authctxt != NULL) {
do_cleanup(the_active_state, the_authctxt);
if (privsep_is_preauth &&
@@ -1395,7 +1393,9 @@ cleanup_exit(int i)
audit_event(the_active_state, SSH_CONNECTION_ABANDON);
#endif
/* Override default fatal exit value when auth was attempted */
if (i == 255 && auth_attempted)
if (i == 255 && monitor_auth_attempted())
_exit(EXIT_AUTH_ATTEMPTED);
if (i == 255 && monitor_invalid_user())
_exit(EXIT_INVALID_USER);
_exit(i);
}

8
sshd.c
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: sshd.c,v 1.623 2025/11/13 10:35:14 dtucker Exp $ */
/* $OpenBSD: sshd.c,v 1.624 2025/12/16 08:32:50 dtucker Exp $ */
/*
* Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved.
* Copyright (c) 2002 Niels Provos. All rights reserved.
@@ -399,6 +399,12 @@ child_reap(struct early_child *child)
"after unsuccessful auth attempt%s",
(long)child->pid, child->id, child_status);
break;
case EXIT_INVALID_USER:
penalty_type = SRCLIMIT_PENALTY_INVALIDUSER;
debug_f("preauth child %ld for %s exited "
"after auth attempt by invalid user%s",
(long)child->pid, child->id, child_status);
break;
case EXIT_CONFIG_REFUSED:
penalty_type = SRCLIMIT_PENALTY_REFUSECONNECTION;
debug_f("preauth child %ld for %s prohibited by"

View File

@@ -33,8 +33,8 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $OpenBSD: sshd_config.5,v 1.389 2025/12/08 03:55:22 djm Exp $
.Dd $Mdocdate: December 8 2025 $
.\" $OpenBSD: sshd_config.5,v 1.390 2025/12/16 08:32:50 dtucker Exp $
.Dd $Mdocdate: December 16 2025 $
.Dt SSHD_CONFIG 5
.Os
.Sh NAME
@@ -1622,6 +1622,9 @@ Specifies how long to refuse clients that cause a crash of
.It Cm authfail:duration
Specifies how long to refuse clients that disconnect after making one or more
unsuccessful authentication attempts (default: 5s).
.It Cm invaliduser:duration
Specifies how long to refuse clients that attempt to log in with an invalid
user (default: 5s).
.It Cm refuseconnection:duration
Specifies how long to refuse clients that were administratively prohibited
connection via the