зеркало из https://github.com/Azure/sonic-openssh.git
upstream: Add PerSourceMaxStartups and PerSourceNetBlockSize
options which provide more fine grained MaxStartups limits. Man page help jmc@, feedback & ok djm@ OpenBSD-Commit-ID: e2f68664e3d02c0895b35aa751c48a2af622047b
This commit is contained in:
Родитель
d9a2bc7169
Коммит
3a92312953
|
@ -125,7 +125,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
|
|||
monitor.o monitor_wrap.o auth-krb5.o \
|
||||
auth2-gss.o gss-serv.o gss-serv-krb5.o \
|
||||
loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
|
||||
sftp-server.o sftp-common.o \
|
||||
srclimit.o sftp-server.o sftp-common.o \
|
||||
sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
|
||||
sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \
|
||||
sandbox-solaris.o uidswap.o $(SKOBJS)
|
||||
|
|
61
servconf.c
61
servconf.c
|
@ -1,5 +1,5 @@
|
|||
|
||||
/* $OpenBSD: servconf.c,v 1.371 2020/10/18 11:32:02 djm Exp $ */
|
||||
/* $OpenBSD: servconf.c,v 1.372 2021/01/09 12:10:02 dtucker Exp $ */
|
||||
/*
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
* All rights reserved
|
||||
|
@ -165,6 +165,9 @@ initialize_server_options(ServerOptions *options)
|
|||
options->max_startups_begin = -1;
|
||||
options->max_startups_rate = -1;
|
||||
options->max_startups = -1;
|
||||
options->per_source_max_startups = -1;
|
||||
options->per_source_masklen_ipv4 = -1;
|
||||
options->per_source_masklen_ipv6 = -1;
|
||||
options->max_authtries = -1;
|
||||
options->max_sessions = -1;
|
||||
options->banner = NULL;
|
||||
|
@ -419,6 +422,12 @@ fill_default_server_options(ServerOptions *options)
|
|||
options->max_startups_rate = 30; /* 30% */
|
||||
if (options->max_startups_begin == -1)
|
||||
options->max_startups_begin = 10;
|
||||
if (options->per_source_max_startups == -1)
|
||||
options->per_source_max_startups = INT_MAX;
|
||||
if (options->per_source_masklen_ipv4 == -1)
|
||||
options->per_source_masklen_ipv4 = 32;
|
||||
if (options->per_source_masklen_ipv6 == -1)
|
||||
options->per_source_masklen_ipv6 = 128;
|
||||
if (options->max_authtries == -1)
|
||||
options->max_authtries = DEFAULT_AUTH_FAIL_MAX;
|
||||
if (options->max_sessions == -1)
|
||||
|
@ -522,7 +531,7 @@ typedef enum {
|
|||
sXAuthLocation, sSubsystem, sMaxStartups, sMaxAuthTries, sMaxSessions,
|
||||
sBanner, sUseDNS, sHostbasedAuthentication,
|
||||
sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes,
|
||||
sHostKeyAlgorithms,
|
||||
sHostKeyAlgorithms, sPerSourceMaxStartups, sPerSourceNetBlockSize,
|
||||
sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
|
||||
sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
|
||||
sAcceptEnv, sSetEnv, sPermitTunnel,
|
||||
|
@ -648,6 +657,8 @@ static struct {
|
|||
{ "gatewayports", sGatewayPorts, SSHCFG_ALL },
|
||||
{ "subsystem", sSubsystem, SSHCFG_GLOBAL },
|
||||
{ "maxstartups", sMaxStartups, SSHCFG_GLOBAL },
|
||||
{ "persourcemaxstartups", sPerSourceMaxStartups, SSHCFG_GLOBAL },
|
||||
{ "persourcenetblocksize", sPerSourceNetBlockSize, SSHCFG_GLOBAL },
|
||||
{ "maxauthtries", sMaxAuthTries, SSHCFG_ALL },
|
||||
{ "maxsessions", sMaxSessions, SSHCFG_ALL },
|
||||
{ "banner", sBanner, SSHCFG_ALL },
|
||||
|
@ -1891,6 +1902,45 @@ process_server_config_line_depth(ServerOptions *options, char *line,
|
|||
options->max_startups = options->max_startups_begin;
|
||||
break;
|
||||
|
||||
case sPerSourceNetBlockSize:
|
||||
arg = strdelim(&cp);
|
||||
if (!arg || *arg == '\0')
|
||||
fatal("%s line %d: Missing PerSourceNetBlockSize spec.",
|
||||
filename, linenum);
|
||||
switch (n = sscanf(arg, "%d:%d", &value, &value2)) {
|
||||
case 2:
|
||||
if (value2 < 0 || value2 > 128)
|
||||
n = -1;
|
||||
/* FALLTHROUGH */
|
||||
case 1:
|
||||
if (value < 0 || value > 32)
|
||||
n = -1;
|
||||
}
|
||||
if (n != 1 && n != 2)
|
||||
fatal("%s line %d: Invalid PerSourceNetBlockSize"
|
||||
" spec.", filename, linenum);
|
||||
if (*activep) {
|
||||
options->per_source_masklen_ipv4 = value;
|
||||
options->per_source_masklen_ipv6 = value2;
|
||||
}
|
||||
break;
|
||||
|
||||
case sPerSourceMaxStartups:
|
||||
arg = strdelim(&cp);
|
||||
if (!arg || *arg == '\0')
|
||||
fatal("%s line %d: Missing PerSourceMaxStartups spec.",
|
||||
filename, linenum);
|
||||
if (strcmp(arg, "none") == 0) { /* no limit */
|
||||
value = INT_MAX;
|
||||
} else {
|
||||
if ((errstr = atoi_err(arg, &value)) != NULL)
|
||||
fatal("%s line %d: integer value %s.",
|
||||
filename, linenum, errstr);
|
||||
}
|
||||
if (*activep)
|
||||
options->per_source_max_startups = value;
|
||||
break;
|
||||
|
||||
case sMaxAuthTries:
|
||||
intptr = &options->max_authtries;
|
||||
goto parse_int;
|
||||
|
@ -2905,6 +2955,13 @@ dump_config(ServerOptions *o)
|
|||
|
||||
printf("maxstartups %d:%d:%d\n", o->max_startups_begin,
|
||||
o->max_startups_rate, o->max_startups);
|
||||
printf("persourcemaxstartups ");
|
||||
if (o->per_source_max_startups == INT_MAX)
|
||||
printf("none\n");
|
||||
else
|
||||
printf("%d\n", o->per_source_max_startups);
|
||||
printf("persourcnetblocksize %d:%d\n", o->per_source_masklen_ipv4,
|
||||
o->per_source_masklen_ipv6);
|
||||
|
||||
s = NULL;
|
||||
for (i = 0; tunmode_desc[i].val != -1; i++) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: servconf.h,v 1.148 2020/10/29 03:13:06 djm Exp $ */
|
||||
/* $OpenBSD: servconf.h,v 1.149 2021/01/09 12:10:02 dtucker Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
|
@ -177,6 +177,9 @@ typedef struct {
|
|||
int max_startups_begin;
|
||||
int max_startups_rate;
|
||||
int max_startups;
|
||||
int per_source_max_startups;
|
||||
int per_source_masklen_ipv4;
|
||||
int per_source_masklen_ipv6;
|
||||
int max_authtries;
|
||||
int max_sessions;
|
||||
char *banner; /* SSH-2 banner message */
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Darren Tucker <dtucker@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "addr.h"
|
||||
#include "canohost.h"
|
||||
#include "log.h"
|
||||
#include "misc.h"
|
||||
#include "srclimit.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
static int max_children, max_persource, ipv4_masklen, ipv6_masklen;
|
||||
|
||||
/* Per connection state, used to enforce unauthenticated connection limit. */
|
||||
static struct child_info {
|
||||
int id;
|
||||
struct xaddr addr;
|
||||
} *child;
|
||||
|
||||
void
|
||||
srclimit_init(int max, int persource, int ipv4len, int ipv6len)
|
||||
{
|
||||
int i;
|
||||
|
||||
max_children = max;
|
||||
ipv4_masklen = ipv4len;
|
||||
ipv6_masklen = ipv6len;
|
||||
max_persource = persource;
|
||||
if (max_persource == INT_MAX) /* no limit */
|
||||
return;
|
||||
debug("%s: max connections %d, per source %d, masks %d,%d", __func__,
|
||||
max, persource, ipv4len, ipv6len);
|
||||
if (max <= 0)
|
||||
fatal("%s: invalid number of sockets: %d", __func__, max);
|
||||
child = xcalloc(max_children, sizeof(*child));
|
||||
for (i = 0; i < max_children; i++)
|
||||
child[i].id = -1;
|
||||
}
|
||||
|
||||
/* returns 1 if connection allowed, 0 if not allowed. */
|
||||
int
|
||||
srclimit_check_allow(int sock, int id)
|
||||
{
|
||||
struct xaddr xa, xb, xmask;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
struct sockaddr *sa = (struct sockaddr *)&addr;
|
||||
int i, bits, first_unused, count = 0;
|
||||
char xas[NI_MAXHOST];
|
||||
|
||||
if (max_persource == INT_MAX) /* no limit */
|
||||
return 1;
|
||||
|
||||
debug("%s: sock %d id %d limit %d", __func__, sock, id, max_persource);
|
||||
if (getpeername(sock, sa, &addrlen) != 0)
|
||||
return 1; /* not remote socket? */
|
||||
if (addr_sa_to_xaddr(sa, addrlen, &xa) != 0)
|
||||
return 1; /* unknown address family? */
|
||||
|
||||
/* Mask address off address to desired size. */
|
||||
bits = xa.af == AF_INET ? ipv4_masklen : ipv6_masklen;
|
||||
if (addr_netmask(xa.af, bits, &xmask) != 0 ||
|
||||
addr_and(&xb, &xa, &xmask) != 0) {
|
||||
debug3("%s: invalid mask %d bits", __func__, bits);
|
||||
return 1;
|
||||
}
|
||||
|
||||
first_unused = max_children;
|
||||
/* Count matching entries and find first unused one. */
|
||||
for (i = 0; i < max_children; i++) {
|
||||
if (child[i].id == -1) {
|
||||
if (i < first_unused)
|
||||
first_unused = i;
|
||||
} else if (addr_cmp(&child[i].addr, &xb) == 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (addr_ntop(&xa, xas, sizeof(xas)) != 0) {
|
||||
debug3("%s: addr ntop failed", __func__);
|
||||
return 1;
|
||||
}
|
||||
debug3("%s: new unauthenticated connection from %s/%d, at %d of %d",
|
||||
__func__, xas, bits, count, max_persource);
|
||||
|
||||
if (first_unused == max_children) { /* no free slot found */
|
||||
debug3("%s: no free slot", __func__);
|
||||
return 0;
|
||||
}
|
||||
if (first_unused < 0 || first_unused >= max_children)
|
||||
fatal("%s: internal error: first_unused out of range",
|
||||
__func__);
|
||||
|
||||
if (count >= max_persource)
|
||||
return 0;
|
||||
|
||||
/* Connection allowed, store masked address. */
|
||||
child[first_unused].id = id;
|
||||
memcpy(&child[first_unused].addr, &xb, sizeof(xb));
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
srclimit_done(int id)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (max_persource == INT_MAX) /* no limit */
|
||||
return;
|
||||
|
||||
debug("%s: id %d", __func__, id);
|
||||
/* Clear corresponding state entry. */
|
||||
for (i = 0; i < max_children; i++) {
|
||||
if (child[i].id == id) {
|
||||
child[i].id = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Darren Tucker <dtucker@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
void srclimit_init(int, int, int, int);
|
||||
int srclimit_check_allow(int, int);
|
||||
void srclimit_done(int);
|
20
sshd.c
20
sshd.c
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: sshd.c,v 1.566 2020/12/29 00:59:15 djm Exp $ */
|
||||
/* $OpenBSD: sshd.c,v 1.567 2021/01/09 12:10:02 dtucker Exp $ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
|
@ -123,6 +123,7 @@
|
|||
#include "version.h"
|
||||
#include "ssherr.h"
|
||||
#include "sk-api.h"
|
||||
#include "srclimit.h"
|
||||
|
||||
/* Re-exec fds */
|
||||
#define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1)
|
||||
|
@ -853,7 +854,7 @@ should_drop_connection(int startups)
|
|||
* while in that state.
|
||||
*/
|
||||
static int
|
||||
drop_connection(int sock, int startups)
|
||||
drop_connection(int sock, int startups, int notify_pipe)
|
||||
{
|
||||
char *laddr, *raddr;
|
||||
const char msg[] = "Exceeded MaxStartups\r\n";
|
||||
|
@ -863,7 +864,8 @@ drop_connection(int sock, int startups)
|
|||
time_t now;
|
||||
|
||||
now = monotime();
|
||||
if (!should_drop_connection(startups)) {
|
||||
if (!should_drop_connection(startups) &&
|
||||
srclimit_check_allow(sock, notify_pipe) == 1) {
|
||||
if (last_drop != 0 &&
|
||||
startups < options.max_startups_begin - 1) {
|
||||
/* XXX maybe need better hysteresis here */
|
||||
|
@ -1109,6 +1111,10 @@ server_listen(void)
|
|||
{
|
||||
u_int i;
|
||||
|
||||
/* 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);
|
||||
|
||||
for (i = 0; i < options.num_listen_addrs; i++) {
|
||||
listen_on_addrs(&options.listen_addrs[i]);
|
||||
freeaddrinfo(options.listen_addrs[i].addrs);
|
||||
|
@ -1215,6 +1221,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
|
|||
case 0:
|
||||
/* child exited or completed auth */
|
||||
close(startup_pipes[i]);
|
||||
srclimit_done(startup_pipes[i]);
|
||||
startup_pipes[i] = -1;
|
||||
startups--;
|
||||
if (startup_flags[i])
|
||||
|
@ -1245,9 +1252,12 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
|
|||
continue;
|
||||
}
|
||||
if (unset_nonblock(*newsock) == -1 ||
|
||||
drop_connection(*newsock, startups) ||
|
||||
pipe(startup_p) == -1) {
|
||||
pipe(startup_p) == -1)
|
||||
continue;
|
||||
if (drop_connection(*newsock, startups, startup_p[0])) {
|
||||
close(*newsock);
|
||||
close(startup_p[0]);
|
||||
close(startup_p[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.320 2021/01/08 02:19:24 djm Exp $
|
||||
.Dd $Mdocdate: January 8 2021 $
|
||||
.\" $OpenBSD: sshd_config.5,v 1.321 2021/01/09 12:10:02 dtucker Exp $
|
||||
.Dd $Mdocdate: January 9 2021 $
|
||||
.Dt SSHD_CONFIG 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -1434,6 +1434,23 @@ SSH daemon, or
|
|||
to not write one.
|
||||
The default is
|
||||
.Pa /var/run/sshd.pid .
|
||||
.It Cm PerSourceMaxStartups
|
||||
Specifies the number of unauthenticated connections allowed from a
|
||||
given source address, or
|
||||
.Dq none
|
||||
if there is no limit.
|
||||
This limit is applied in addition to
|
||||
.Cm MaxStartups ,
|
||||
whichever is lower.
|
||||
The default is
|
||||
.Cm none .
|
||||
.It Cm PerSourceNetBlockSize
|
||||
Specifies the number of bits of source address that are grouped together
|
||||
for the purposes of applying PerSourceMaxStartups limits.
|
||||
Values for IPv4 and optionally IPv6 may be specified, separated by a colon.
|
||||
The default is
|
||||
.Cm 32:128
|
||||
which means each address is considered individually.
|
||||
.It Cm Port
|
||||
Specifies the port number that
|
||||
.Xr sshd 8
|
||||
|
|
Загрузка…
Ссылка в новой задаче