119261079SEd Maste /*
219261079SEd Maste * Copyright (c) 2020 Darren Tucker <dtucker@openbsd.org>
30fdf8faeSEd Maste * Copyright (c) 2024 Damien Miller <djm@mindrot.org>
419261079SEd Maste *
519261079SEd Maste * Permission to use, copy, modify, and distribute this software for any
619261079SEd Maste * purpose with or without fee is hereby granted, provided that the above
719261079SEd Maste * copyright notice and this permission notice appear in all copies.
819261079SEd Maste *
919261079SEd Maste * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1019261079SEd Maste * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1119261079SEd Maste * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1219261079SEd Maste * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1319261079SEd Maste * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1419261079SEd Maste * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1519261079SEd Maste * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1619261079SEd Maste */
1719261079SEd Maste
1819261079SEd Maste #include "includes.h"
1919261079SEd Maste
2019261079SEd Maste #include <sys/socket.h>
2119261079SEd Maste #include <sys/types.h>
220fdf8faeSEd Maste #include <openbsd-compat/sys-tree.h>
2319261079SEd Maste
2419261079SEd Maste #include <limits.h>
2519261079SEd Maste #include <netdb.h>
2619261079SEd Maste #include <stdio.h>
2719261079SEd Maste #include <string.h>
280fdf8faeSEd Maste #include <stdlib.h>
2919261079SEd Maste
3019261079SEd Maste #include "addr.h"
3119261079SEd Maste #include "canohost.h"
3219261079SEd Maste #include "log.h"
3319261079SEd Maste #include "misc.h"
3419261079SEd Maste #include "srclimit.h"
3519261079SEd Maste #include "xmalloc.h"
360fdf8faeSEd Maste #include "servconf.h"
370fdf8faeSEd Maste #include "match.h"
3819261079SEd Maste
3919261079SEd Maste static int max_children, max_persource, ipv4_masklen, ipv6_masklen;
400fdf8faeSEd Maste static struct per_source_penalty penalty_cfg;
410fdf8faeSEd Maste static char *penalty_exempt;
4219261079SEd Maste
4319261079SEd Maste /* Per connection state, used to enforce unauthenticated connection limit. */
4419261079SEd Maste static struct child_info {
4519261079SEd Maste int id;
4619261079SEd Maste struct xaddr addr;
470fdf8faeSEd Maste } *children;
480fdf8faeSEd Maste
490fdf8faeSEd Maste /*
500fdf8faeSEd Maste * Penalised addresses, active entries here prohibit connections until expired.
510fdf8faeSEd Maste * Entries become active when more than penalty_min seconds of penalty are
520fdf8faeSEd Maste * outstanding.
530fdf8faeSEd Maste */
540fdf8faeSEd Maste struct penalty {
550fdf8faeSEd Maste struct xaddr addr;
560fdf8faeSEd Maste time_t expiry;
570fdf8faeSEd Maste int active;
580fdf8faeSEd Maste const char *reason;
590fdf8faeSEd Maste RB_ENTRY(penalty) by_addr;
600fdf8faeSEd Maste RB_ENTRY(penalty) by_expiry;
610fdf8faeSEd Maste };
620fdf8faeSEd Maste static int penalty_addr_cmp(struct penalty *a, struct penalty *b);
630fdf8faeSEd Maste static int penalty_expiry_cmp(struct penalty *a, struct penalty *b);
640fdf8faeSEd Maste RB_HEAD(penalties_by_addr, penalty) penalties_by_addr4, penalties_by_addr6;
650fdf8faeSEd Maste RB_HEAD(penalties_by_expiry, penalty) penalties_by_expiry4, penalties_by_expiry6;
660fdf8faeSEd Maste RB_GENERATE_STATIC(penalties_by_addr, penalty, by_addr, penalty_addr_cmp)
670fdf8faeSEd Maste RB_GENERATE_STATIC(penalties_by_expiry, penalty, by_expiry, penalty_expiry_cmp)
680fdf8faeSEd Maste static size_t npenalties4, npenalties6;
690fdf8faeSEd Maste
700fdf8faeSEd Maste static int
srclimit_mask_addr(const struct xaddr * addr,int bits,struct xaddr * masked)710fdf8faeSEd Maste srclimit_mask_addr(const struct xaddr *addr, int bits, struct xaddr *masked)
720fdf8faeSEd Maste {
730fdf8faeSEd Maste struct xaddr xmask;
740fdf8faeSEd Maste
750fdf8faeSEd Maste /* Mask address off address to desired size. */
760fdf8faeSEd Maste if (addr_netmask(addr->af, bits, &xmask) != 0 ||
770fdf8faeSEd Maste addr_and(masked, addr, &xmask) != 0) {
780fdf8faeSEd Maste debug3_f("%s: invalid mask %d bits", __func__, bits);
790fdf8faeSEd Maste return -1;
800fdf8faeSEd Maste }
810fdf8faeSEd Maste return 0;
820fdf8faeSEd Maste }
830fdf8faeSEd Maste
840fdf8faeSEd Maste static int
srclimit_peer_addr(int sock,struct xaddr * addr)850fdf8faeSEd Maste srclimit_peer_addr(int sock, struct xaddr *addr)
860fdf8faeSEd Maste {
870fdf8faeSEd Maste struct sockaddr_storage storage;
880fdf8faeSEd Maste socklen_t addrlen = sizeof(storage);
890fdf8faeSEd Maste struct sockaddr *sa = (struct sockaddr *)&storage;
900fdf8faeSEd Maste
910fdf8faeSEd Maste if (getpeername(sock, sa, &addrlen) != 0)
920fdf8faeSEd Maste return 1; /* not remote socket? */
930fdf8faeSEd Maste if (addr_sa_to_xaddr(sa, addrlen, addr) != 0)
940fdf8faeSEd Maste return 1; /* unknown address family? */
950fdf8faeSEd Maste return 0;
960fdf8faeSEd Maste }
9719261079SEd Maste
9819261079SEd Maste void
srclimit_init(int max,int persource,int ipv4len,int ipv6len,struct per_source_penalty * penalty_conf,const char * penalty_exempt_conf)990fdf8faeSEd Maste srclimit_init(int max, int persource, int ipv4len, int ipv6len,
1000fdf8faeSEd Maste struct per_source_penalty *penalty_conf, const char *penalty_exempt_conf)
10119261079SEd Maste {
10219261079SEd Maste int i;
10319261079SEd Maste
10419261079SEd Maste max_children = max;
10519261079SEd Maste ipv4_masklen = ipv4len;
10619261079SEd Maste ipv6_masklen = ipv6len;
10719261079SEd Maste max_persource = persource;
1080fdf8faeSEd Maste penalty_cfg = *penalty_conf;
1090fdf8faeSEd Maste if (penalty_cfg.max_sources4 < 0 || penalty_cfg.max_sources6 < 0)
1100fdf8faeSEd Maste fatal_f("invalid max_sources"); /* shouldn't happen */
1110fdf8faeSEd Maste penalty_exempt = penalty_exempt_conf == NULL ?
1120fdf8faeSEd Maste NULL : xstrdup(penalty_exempt_conf);
1130fdf8faeSEd Maste RB_INIT(&penalties_by_addr4);
1140fdf8faeSEd Maste RB_INIT(&penalties_by_expiry4);
1150fdf8faeSEd Maste RB_INIT(&penalties_by_addr6);
1160fdf8faeSEd Maste RB_INIT(&penalties_by_expiry6);
11719261079SEd Maste if (max_persource == INT_MAX) /* no limit */
11819261079SEd Maste return;
11919261079SEd Maste debug("%s: max connections %d, per source %d, masks %d,%d", __func__,
12019261079SEd Maste max, persource, ipv4len, ipv6len);
12119261079SEd Maste if (max <= 0)
12219261079SEd Maste fatal("%s: invalid number of sockets: %d", __func__, max);
1230fdf8faeSEd Maste children = xcalloc(max_children, sizeof(*children));
12419261079SEd Maste for (i = 0; i < max_children; i++)
1250fdf8faeSEd Maste children[i].id = -1;
12619261079SEd Maste }
12719261079SEd Maste
12819261079SEd Maste /* returns 1 if connection allowed, 0 if not allowed. */
12919261079SEd Maste int
srclimit_check_allow(int sock,int id)13019261079SEd Maste srclimit_check_allow(int sock, int id)
13119261079SEd Maste {
1320fdf8faeSEd Maste struct xaddr xa, xb;
13319261079SEd Maste int i, bits, first_unused, count = 0;
13419261079SEd Maste char xas[NI_MAXHOST];
13519261079SEd Maste
13619261079SEd Maste if (max_persource == INT_MAX) /* no limit */
13719261079SEd Maste return 1;
13819261079SEd Maste
13919261079SEd Maste debug("%s: sock %d id %d limit %d", __func__, sock, id, max_persource);
1400fdf8faeSEd Maste if (srclimit_peer_addr(sock, &xa) != 0)
14119261079SEd Maste return 1;
1420fdf8faeSEd Maste bits = xa.af == AF_INET ? ipv4_masklen : ipv6_masklen;
1430fdf8faeSEd Maste if (srclimit_mask_addr(&xa, bits, &xb) != 0)
1440fdf8faeSEd Maste return 1;
14519261079SEd Maste
14619261079SEd Maste first_unused = max_children;
14719261079SEd Maste /* Count matching entries and find first unused one. */
14819261079SEd Maste for (i = 0; i < max_children; i++) {
1490fdf8faeSEd Maste if (children[i].id == -1) {
15019261079SEd Maste if (i < first_unused)
15119261079SEd Maste first_unused = i;
1520fdf8faeSEd Maste } else if (addr_cmp(&children[i].addr, &xb) == 0) {
15319261079SEd Maste count++;
15419261079SEd Maste }
15519261079SEd Maste }
15619261079SEd Maste if (addr_ntop(&xa, xas, sizeof(xas)) != 0) {
15719261079SEd Maste debug3("%s: addr ntop failed", __func__);
15819261079SEd Maste return 1;
15919261079SEd Maste }
16019261079SEd Maste debug3("%s: new unauthenticated connection from %s/%d, at %d of %d",
16119261079SEd Maste __func__, xas, bits, count, max_persource);
16219261079SEd Maste
16319261079SEd Maste if (first_unused == max_children) { /* no free slot found */
16419261079SEd Maste debug3("%s: no free slot", __func__);
16519261079SEd Maste return 0;
16619261079SEd Maste }
16719261079SEd Maste if (first_unused < 0 || first_unused >= max_children)
16819261079SEd Maste fatal("%s: internal error: first_unused out of range",
16919261079SEd Maste __func__);
17019261079SEd Maste
17119261079SEd Maste if (count >= max_persource)
17219261079SEd Maste return 0;
17319261079SEd Maste
17419261079SEd Maste /* Connection allowed, store masked address. */
1750fdf8faeSEd Maste children[first_unused].id = id;
1760fdf8faeSEd Maste memcpy(&children[first_unused].addr, &xb, sizeof(xb));
17719261079SEd Maste return 1;
17819261079SEd Maste }
17919261079SEd Maste
18019261079SEd Maste void
srclimit_done(int id)18119261079SEd Maste srclimit_done(int id)
18219261079SEd Maste {
18319261079SEd Maste int i;
18419261079SEd Maste
18519261079SEd Maste if (max_persource == INT_MAX) /* no limit */
18619261079SEd Maste return;
18719261079SEd Maste
18819261079SEd Maste debug("%s: id %d", __func__, id);
18919261079SEd Maste /* Clear corresponding state entry. */
19019261079SEd Maste for (i = 0; i < max_children; i++) {
1910fdf8faeSEd Maste if (children[i].id == id) {
1920fdf8faeSEd Maste children[i].id = -1;
19319261079SEd Maste return;
19419261079SEd Maste }
19519261079SEd Maste }
19619261079SEd Maste }
1970fdf8faeSEd Maste
1980fdf8faeSEd Maste static int
penalty_addr_cmp(struct penalty * a,struct penalty * b)1990fdf8faeSEd Maste penalty_addr_cmp(struct penalty *a, struct penalty *b)
2000fdf8faeSEd Maste {
2010fdf8faeSEd Maste return addr_cmp(&a->addr, &b->addr);
2020fdf8faeSEd Maste /* Addresses must be unique in by_addr, so no need to tiebreak */
2030fdf8faeSEd Maste }
2040fdf8faeSEd Maste
2050fdf8faeSEd Maste static int
penalty_expiry_cmp(struct penalty * a,struct penalty * b)2060fdf8faeSEd Maste penalty_expiry_cmp(struct penalty *a, struct penalty *b)
2070fdf8faeSEd Maste {
2080fdf8faeSEd Maste if (a->expiry != b->expiry)
2090fdf8faeSEd Maste return a->expiry < b->expiry ? -1 : 1;
2100fdf8faeSEd Maste /* Tiebreak on addresses */
2110fdf8faeSEd Maste return addr_cmp(&a->addr, &b->addr);
2120fdf8faeSEd Maste }
2130fdf8faeSEd Maste
2140fdf8faeSEd Maste static void
expire_penalties_from_tree(time_t now,const char * t,struct penalties_by_expiry * by_expiry,struct penalties_by_addr * by_addr,size_t * npenaltiesp)2150fdf8faeSEd Maste expire_penalties_from_tree(time_t now, const char *t,
2160fdf8faeSEd Maste struct penalties_by_expiry *by_expiry,
2170fdf8faeSEd Maste struct penalties_by_addr *by_addr, size_t *npenaltiesp)
2180fdf8faeSEd Maste {
2190fdf8faeSEd Maste struct penalty *penalty, *tmp;
2200fdf8faeSEd Maste
2210fdf8faeSEd Maste /* XXX avoid full scan of tree, e.g. min-heap */
2220fdf8faeSEd Maste RB_FOREACH_SAFE(penalty, penalties_by_expiry, by_expiry, tmp) {
2230fdf8faeSEd Maste if (penalty->expiry >= now)
2240fdf8faeSEd Maste break;
2250fdf8faeSEd Maste if (RB_REMOVE(penalties_by_expiry, by_expiry,
2260fdf8faeSEd Maste penalty) != penalty ||
2270fdf8faeSEd Maste RB_REMOVE(penalties_by_addr, by_addr,
2280fdf8faeSEd Maste penalty) != penalty)
2290fdf8faeSEd Maste fatal_f("internal error: %s penalty table corrupt", t);
2300fdf8faeSEd Maste free(penalty);
2310fdf8faeSEd Maste if ((*npenaltiesp)-- == 0)
2320fdf8faeSEd Maste fatal_f("internal error: %s npenalties underflow", t);
2330fdf8faeSEd Maste }
2340fdf8faeSEd Maste }
2350fdf8faeSEd Maste
2360fdf8faeSEd Maste static void
expire_penalties(time_t now)2370fdf8faeSEd Maste expire_penalties(time_t now)
2380fdf8faeSEd Maste {
2390fdf8faeSEd Maste expire_penalties_from_tree(now, "ipv4",
2400fdf8faeSEd Maste &penalties_by_expiry4, &penalties_by_addr4, &npenalties4);
2410fdf8faeSEd Maste expire_penalties_from_tree(now, "ipv6",
2420fdf8faeSEd Maste &penalties_by_expiry6, &penalties_by_addr6, &npenalties6);
2430fdf8faeSEd Maste }
2440fdf8faeSEd Maste
2450fdf8faeSEd Maste static void
addr_masklen_ntop(struct xaddr * addr,int masklen,char * s,size_t slen)2460fdf8faeSEd Maste addr_masklen_ntop(struct xaddr *addr, int masklen, char *s, size_t slen)
2470fdf8faeSEd Maste {
2480fdf8faeSEd Maste size_t o;
2490fdf8faeSEd Maste
2500fdf8faeSEd Maste if (addr_ntop(addr, s, slen) != 0) {
2510fdf8faeSEd Maste strlcpy(s, "UNKNOWN", slen);
2520fdf8faeSEd Maste return;
2530fdf8faeSEd Maste }
2540fdf8faeSEd Maste if ((o = strlen(s)) < slen)
2550fdf8faeSEd Maste snprintf(s + o, slen - o, "/%d", masklen);
2560fdf8faeSEd Maste }
2570fdf8faeSEd Maste
2580fdf8faeSEd Maste int
srclimit_penalty_check_allow(int sock,const char ** reason)2590fdf8faeSEd Maste srclimit_penalty_check_allow(int sock, const char **reason)
2600fdf8faeSEd Maste {
2610fdf8faeSEd Maste struct xaddr addr;
2620fdf8faeSEd Maste struct penalty find, *penalty;
2630fdf8faeSEd Maste time_t now;
2640fdf8faeSEd Maste int bits, max_sources, overflow_mode;
2650fdf8faeSEd Maste char addr_s[NI_MAXHOST];
2660fdf8faeSEd Maste struct penalties_by_addr *by_addr;
2670fdf8faeSEd Maste size_t npenalties;
2680fdf8faeSEd Maste
2690fdf8faeSEd Maste if (!penalty_cfg.enabled)
2700fdf8faeSEd Maste return 1;
2710fdf8faeSEd Maste if (srclimit_peer_addr(sock, &addr) != 0)
2720fdf8faeSEd Maste return 1;
2730fdf8faeSEd Maste if (penalty_exempt != NULL) {
2740fdf8faeSEd Maste if (addr_ntop(&addr, addr_s, sizeof(addr_s)) != 0)
2750fdf8faeSEd Maste return 1; /* shouldn't happen */
2760fdf8faeSEd Maste if (addr_match_list(addr_s, penalty_exempt) == 1) {
2770fdf8faeSEd Maste return 1;
2780fdf8faeSEd Maste }
2790fdf8faeSEd Maste }
2800fdf8faeSEd Maste now = monotime();
2810fdf8faeSEd Maste expire_penalties(now);
2820fdf8faeSEd Maste by_addr = addr.af == AF_INET ?
2830fdf8faeSEd Maste &penalties_by_addr4 : &penalties_by_addr6;
2840fdf8faeSEd Maste max_sources = addr.af == AF_INET ?
2850fdf8faeSEd Maste penalty_cfg.max_sources4 : penalty_cfg.max_sources6;
2860fdf8faeSEd Maste overflow_mode = addr.af == AF_INET ?
2870fdf8faeSEd Maste penalty_cfg.overflow_mode : penalty_cfg.overflow_mode6;
2880fdf8faeSEd Maste npenalties = addr.af == AF_INET ? npenalties4 : npenalties6;
2890fdf8faeSEd Maste if (npenalties >= (size_t)max_sources &&
2900fdf8faeSEd Maste overflow_mode == PER_SOURCE_PENALTY_OVERFLOW_DENY_ALL) {
2910fdf8faeSEd Maste *reason = "too many penalised addresses";
2920fdf8faeSEd Maste return 0;
2930fdf8faeSEd Maste }
2940fdf8faeSEd Maste bits = addr.af == AF_INET ? ipv4_masklen : ipv6_masklen;
2950fdf8faeSEd Maste memset(&find, 0, sizeof(find));
2960fdf8faeSEd Maste if (srclimit_mask_addr(&addr, bits, &find.addr) != 0)
2970fdf8faeSEd Maste return 1;
2980fdf8faeSEd Maste if ((penalty = RB_FIND(penalties_by_addr, by_addr, &find)) == NULL)
2990fdf8faeSEd Maste return 1; /* no penalty */
3000fdf8faeSEd Maste if (penalty->expiry < now) {
3010fdf8faeSEd Maste expire_penalties(now);
3020fdf8faeSEd Maste return 1; /* expired penalty */
3030fdf8faeSEd Maste }
3040fdf8faeSEd Maste if (!penalty->active)
3050fdf8faeSEd Maste return 1; /* Penalty hasn't hit activation threshold yet */
3060fdf8faeSEd Maste *reason = penalty->reason;
3070fdf8faeSEd Maste return 0;
3080fdf8faeSEd Maste }
3090fdf8faeSEd Maste
3100fdf8faeSEd Maste static void
srclimit_early_expire_penalties_from_tree(const char * t,struct penalties_by_expiry * by_expiry,struct penalties_by_addr * by_addr,size_t * npenaltiesp,size_t max_sources)3110fdf8faeSEd Maste srclimit_early_expire_penalties_from_tree(const char *t,
3120fdf8faeSEd Maste struct penalties_by_expiry *by_expiry,
3130fdf8faeSEd Maste struct penalties_by_addr *by_addr, size_t *npenaltiesp, size_t max_sources)
3140fdf8faeSEd Maste {
3150fdf8faeSEd Maste struct penalty *p = NULL;
3160fdf8faeSEd Maste int bits;
3170fdf8faeSEd Maste char s[NI_MAXHOST + 4];
3180fdf8faeSEd Maste
3190fdf8faeSEd Maste /* Delete the soonest-to-expire penalties. */
3200fdf8faeSEd Maste while (*npenaltiesp > max_sources) {
3210fdf8faeSEd Maste if ((p = RB_MIN(penalties_by_expiry, by_expiry)) == NULL)
3220fdf8faeSEd Maste fatal_f("internal error: %s table corrupt (find)", t);
3230fdf8faeSEd Maste bits = p->addr.af == AF_INET ? ipv4_masklen : ipv6_masklen;
3240fdf8faeSEd Maste addr_masklen_ntop(&p->addr, bits, s, sizeof(s));
3250fdf8faeSEd Maste debug3_f("%s overflow, remove %s", t, s);
3260fdf8faeSEd Maste if (RB_REMOVE(penalties_by_expiry, by_expiry, p) != p ||
3270fdf8faeSEd Maste RB_REMOVE(penalties_by_addr, by_addr, p) != p)
3280fdf8faeSEd Maste fatal_f("internal error: %s table corrupt (remove)", t);
3290fdf8faeSEd Maste free(p);
3300fdf8faeSEd Maste (*npenaltiesp)--;
3310fdf8faeSEd Maste }
3320fdf8faeSEd Maste }
3330fdf8faeSEd Maste
3340fdf8faeSEd Maste static void
srclimit_early_expire_penalties(void)3350fdf8faeSEd Maste srclimit_early_expire_penalties(void)
3360fdf8faeSEd Maste {
3370fdf8faeSEd Maste srclimit_early_expire_penalties_from_tree("ipv4",
3380fdf8faeSEd Maste &penalties_by_expiry4, &penalties_by_addr4, &npenalties4,
3390fdf8faeSEd Maste (size_t)penalty_cfg.max_sources4);
3400fdf8faeSEd Maste srclimit_early_expire_penalties_from_tree("ipv6",
3410fdf8faeSEd Maste &penalties_by_expiry6, &penalties_by_addr6, &npenalties6,
3420fdf8faeSEd Maste (size_t)penalty_cfg.max_sources6);
3430fdf8faeSEd Maste }
3440fdf8faeSEd Maste
3450fdf8faeSEd Maste void
srclimit_penalise(struct xaddr * addr,int penalty_type)3460fdf8faeSEd Maste srclimit_penalise(struct xaddr *addr, int penalty_type)
3470fdf8faeSEd Maste {
3480fdf8faeSEd Maste struct xaddr masked;
3490fdf8faeSEd Maste struct penalty *penalty = NULL, *existing = NULL;
3500fdf8faeSEd Maste time_t now;
3510fdf8faeSEd Maste int bits, penalty_secs, max_sources = 0, overflow_mode;
3520fdf8faeSEd Maste char addrnetmask[NI_MAXHOST + 4];
3530fdf8faeSEd Maste const char *reason = NULL, *t;
3540fdf8faeSEd Maste size_t *npenaltiesp = NULL;
3550fdf8faeSEd Maste struct penalties_by_addr *by_addr = NULL;
3560fdf8faeSEd Maste struct penalties_by_expiry *by_expiry = NULL;
3570fdf8faeSEd Maste
3580fdf8faeSEd Maste if (!penalty_cfg.enabled)
3590fdf8faeSEd Maste return;
3600fdf8faeSEd Maste if (penalty_exempt != NULL) {
3610fdf8faeSEd Maste if (addr_ntop(addr, addrnetmask, sizeof(addrnetmask)) != 0)
3620fdf8faeSEd Maste return; /* shouldn't happen */
3630fdf8faeSEd Maste if (addr_match_list(addrnetmask, penalty_exempt) == 1) {
3640fdf8faeSEd Maste debug3_f("address %s is exempt", addrnetmask);
3650fdf8faeSEd Maste return;
3660fdf8faeSEd Maste }
3670fdf8faeSEd Maste }
3680fdf8faeSEd Maste
3690fdf8faeSEd Maste switch (penalty_type) {
3700fdf8faeSEd Maste case SRCLIMIT_PENALTY_NONE:
3710fdf8faeSEd Maste return;
3720fdf8faeSEd Maste case SRCLIMIT_PENALTY_CRASH:
3730fdf8faeSEd Maste penalty_secs = penalty_cfg.penalty_crash;
3740fdf8faeSEd Maste reason = "penalty: caused crash";
3750fdf8faeSEd Maste break;
3760fdf8faeSEd Maste case SRCLIMIT_PENALTY_AUTHFAIL:
3770fdf8faeSEd Maste penalty_secs = penalty_cfg.penalty_authfail;
3780fdf8faeSEd Maste reason = "penalty: failed authentication";
3790fdf8faeSEd Maste break;
3800fdf8faeSEd Maste case SRCLIMIT_PENALTY_NOAUTH:
3810fdf8faeSEd Maste penalty_secs = penalty_cfg.penalty_noauth;
3820fdf8faeSEd Maste reason = "penalty: connections without attempting authentication";
3830fdf8faeSEd Maste break;
384*3d9fd9fcSEd Maste case SRCLIMIT_PENALTY_REFUSECONNECTION:
385*3d9fd9fcSEd Maste penalty_secs = penalty_cfg.penalty_refuseconnection;
386*3d9fd9fcSEd Maste reason = "penalty: connection prohibited by RefuseConnection";
387*3d9fd9fcSEd Maste break;
3880fdf8faeSEd Maste case SRCLIMIT_PENALTY_GRACE_EXCEEDED:
3890fdf8faeSEd Maste penalty_secs = penalty_cfg.penalty_crash;
3900fdf8faeSEd Maste reason = "penalty: exceeded LoginGraceTime";
3910fdf8faeSEd Maste break;
3920fdf8faeSEd Maste default:
3930fdf8faeSEd Maste fatal_f("internal error: unknown penalty %d", penalty_type);
3940fdf8faeSEd Maste }
3950fdf8faeSEd Maste bits = addr->af == AF_INET ? ipv4_masklen : ipv6_masklen;
3960fdf8faeSEd Maste if (srclimit_mask_addr(addr, bits, &masked) != 0)
3970fdf8faeSEd Maste return;
3980fdf8faeSEd Maste addr_masklen_ntop(addr, bits, addrnetmask, sizeof(addrnetmask));
3990fdf8faeSEd Maste
4000fdf8faeSEd Maste now = monotime();
4010fdf8faeSEd Maste expire_penalties(now);
4020fdf8faeSEd Maste by_expiry = addr->af == AF_INET ?
4030fdf8faeSEd Maste &penalties_by_expiry4 : &penalties_by_expiry6;
4040fdf8faeSEd Maste by_addr = addr->af == AF_INET ?
4050fdf8faeSEd Maste &penalties_by_addr4 : &penalties_by_addr6;
4060fdf8faeSEd Maste max_sources = addr->af == AF_INET ?
4070fdf8faeSEd Maste penalty_cfg.max_sources4 : penalty_cfg.max_sources6;
4080fdf8faeSEd Maste overflow_mode = addr->af == AF_INET ?
4090fdf8faeSEd Maste penalty_cfg.overflow_mode : penalty_cfg.overflow_mode6;
4100fdf8faeSEd Maste npenaltiesp = addr->af == AF_INET ? &npenalties4 : &npenalties6;
4110fdf8faeSEd Maste t = addr->af == AF_INET ? "ipv4" : "ipv6";
4120fdf8faeSEd Maste if (*npenaltiesp >= (size_t)max_sources &&
4130fdf8faeSEd Maste overflow_mode == PER_SOURCE_PENALTY_OVERFLOW_DENY_ALL) {
4140fdf8faeSEd Maste verbose_f("%s penalty table full, cannot penalise %s for %s", t,
4150fdf8faeSEd Maste addrnetmask, reason);
4160fdf8faeSEd Maste return;
4170fdf8faeSEd Maste }
4180fdf8faeSEd Maste
4190fdf8faeSEd Maste penalty = xcalloc(1, sizeof(*penalty));
4200fdf8faeSEd Maste penalty->addr = masked;
4210fdf8faeSEd Maste penalty->expiry = now + penalty_secs;
4220fdf8faeSEd Maste penalty->reason = reason;
4230fdf8faeSEd Maste if ((existing = RB_INSERT(penalties_by_addr, by_addr,
4240fdf8faeSEd Maste penalty)) == NULL) {
4250fdf8faeSEd Maste /* penalty didn't previously exist */
4260fdf8faeSEd Maste if (penalty_secs > penalty_cfg.penalty_min)
4270fdf8faeSEd Maste penalty->active = 1;
4280fdf8faeSEd Maste if (RB_INSERT(penalties_by_expiry, by_expiry, penalty) != NULL)
4290fdf8faeSEd Maste fatal_f("internal error: %s penalty tables corrupt", t);
4300fdf8faeSEd Maste verbose_f("%s: new %s %s penalty of %d seconds for %s", t,
4310fdf8faeSEd Maste addrnetmask, penalty->active ? "active" : "deferred",
4320fdf8faeSEd Maste penalty_secs, reason);
4330fdf8faeSEd Maste if (++(*npenaltiesp) > (size_t)max_sources)
4340fdf8faeSEd Maste srclimit_early_expire_penalties(); /* permissive */
4350fdf8faeSEd Maste return;
4360fdf8faeSEd Maste }
4370fdf8faeSEd Maste debug_f("%s penalty for %s %s already exists, %lld seconds remaining",
4380fdf8faeSEd Maste existing->active ? "active" : "inactive", t,
4390fdf8faeSEd Maste addrnetmask, (long long)(existing->expiry - now));
4400fdf8faeSEd Maste /* Expiry information is about to change, remove from tree */
4410fdf8faeSEd Maste if (RB_REMOVE(penalties_by_expiry, by_expiry, existing) != existing)
4420fdf8faeSEd Maste fatal_f("internal error: %s penalty table corrupt (remove)", t);
4430fdf8faeSEd Maste /* An entry already existed. Accumulate penalty up to maximum */
4440fdf8faeSEd Maste existing->expiry += penalty_secs;
4450fdf8faeSEd Maste if (existing->expiry - now > penalty_cfg.penalty_max)
4460fdf8faeSEd Maste existing->expiry = now + penalty_cfg.penalty_max;
4470fdf8faeSEd Maste if (existing->expiry - now > penalty_cfg.penalty_min &&
4480fdf8faeSEd Maste !existing->active) {
4490fdf8faeSEd Maste verbose_f("%s: activating %s penalty of %lld seconds for %s",
4500fdf8faeSEd Maste addrnetmask, t, (long long)(existing->expiry - now),
4510fdf8faeSEd Maste reason);
4520fdf8faeSEd Maste existing->active = 1;
4530fdf8faeSEd Maste }
4540fdf8faeSEd Maste existing->reason = penalty->reason;
4550fdf8faeSEd Maste free(penalty);
4560fdf8faeSEd Maste penalty = NULL;
4570fdf8faeSEd Maste /* Re-insert into expiry tree */
4580fdf8faeSEd Maste if (RB_INSERT(penalties_by_expiry, by_expiry, existing) != NULL)
4590fdf8faeSEd Maste fatal_f("internal error: %s penalty table corrupt (insert)", t);
4600fdf8faeSEd Maste }
4610fdf8faeSEd Maste
4620fdf8faeSEd Maste static void
srclimit_penalty_info_for_tree(const char * t,struct penalties_by_expiry * by_expiry,size_t npenalties)4630fdf8faeSEd Maste srclimit_penalty_info_for_tree(const char *t,
4640fdf8faeSEd Maste struct penalties_by_expiry *by_expiry, size_t npenalties)
4650fdf8faeSEd Maste {
4660fdf8faeSEd Maste struct penalty *p = NULL;
4670fdf8faeSEd Maste int bits;
4680fdf8faeSEd Maste char s[NI_MAXHOST + 4];
4690fdf8faeSEd Maste time_t now;
4700fdf8faeSEd Maste
4710fdf8faeSEd Maste now = monotime();
4720fdf8faeSEd Maste logit("%zu active %s penalties", npenalties, t);
4730fdf8faeSEd Maste RB_FOREACH(p, penalties_by_expiry, by_expiry) {
4740fdf8faeSEd Maste bits = p->addr.af == AF_INET ? ipv4_masklen : ipv6_masklen;
4750fdf8faeSEd Maste addr_masklen_ntop(&p->addr, bits, s, sizeof(s));
4760fdf8faeSEd Maste if (p->expiry < now)
4770fdf8faeSEd Maste logit("client %s %s (expired)", s, p->reason);
4780fdf8faeSEd Maste else {
4790fdf8faeSEd Maste logit("client %s %s (%llu secs left)", s, p->reason,
4800fdf8faeSEd Maste (long long)(p->expiry - now));
4810fdf8faeSEd Maste }
4820fdf8faeSEd Maste }
4830fdf8faeSEd Maste }
4840fdf8faeSEd Maste
4850fdf8faeSEd Maste void
srclimit_penalty_info(void)4860fdf8faeSEd Maste srclimit_penalty_info(void)
4870fdf8faeSEd Maste {
4880fdf8faeSEd Maste srclimit_penalty_info_for_tree("ipv4",
4890fdf8faeSEd Maste &penalties_by_expiry4, npenalties4);
4900fdf8faeSEd Maste srclimit_penalty_info_for_tree("ipv6",
4910fdf8faeSEd Maste &penalties_by_expiry6, npenalties6);
4920fdf8faeSEd Maste }
493