xref: /freebsd/crypto/openssh/srclimit.c (revision 3d9fd9fcb432750f3716b28f6ccb0104cd9d351a)
1 /*
2  * Copyright (c) 2020 Darren Tucker <dtucker@openbsd.org>
3  * Copyright (c) 2024 Damien Miller <djm@mindrot.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include "includes.h"
19 
20 #include <sys/socket.h>
21 #include <sys/types.h>
22 #include <openbsd-compat/sys-tree.h>
23 
24 #include <limits.h>
25 #include <netdb.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 
30 #include "addr.h"
31 #include "canohost.h"
32 #include "log.h"
33 #include "misc.h"
34 #include "srclimit.h"
35 #include "xmalloc.h"
36 #include "servconf.h"
37 #include "match.h"
38 
39 static int max_children, max_persource, ipv4_masklen, ipv6_masklen;
40 static struct per_source_penalty penalty_cfg;
41 static char *penalty_exempt;
42 
43 /* Per connection state, used to enforce unauthenticated connection limit. */
44 static struct child_info {
45 	int id;
46 	struct xaddr addr;
47 } *children;
48 
49 /*
50  * Penalised addresses, active entries here prohibit connections until expired.
51  * Entries become active when more than penalty_min seconds of penalty are
52  * outstanding.
53  */
54 struct penalty {
55 	struct xaddr addr;
56 	time_t expiry;
57 	int active;
58 	const char *reason;
59 	RB_ENTRY(penalty) by_addr;
60 	RB_ENTRY(penalty) by_expiry;
61 };
62 static int penalty_addr_cmp(struct penalty *a, struct penalty *b);
63 static int penalty_expiry_cmp(struct penalty *a, struct penalty *b);
64 RB_HEAD(penalties_by_addr, penalty) penalties_by_addr4, penalties_by_addr6;
65 RB_HEAD(penalties_by_expiry, penalty) penalties_by_expiry4, penalties_by_expiry6;
66 RB_GENERATE_STATIC(penalties_by_addr, penalty, by_addr, penalty_addr_cmp)
67 RB_GENERATE_STATIC(penalties_by_expiry, penalty, by_expiry, penalty_expiry_cmp)
68 static size_t npenalties4, npenalties6;
69 
70 static int
srclimit_mask_addr(const struct xaddr * addr,int bits,struct xaddr * masked)71 srclimit_mask_addr(const struct xaddr *addr, int bits, struct xaddr *masked)
72 {
73 	struct xaddr xmask;
74 
75 	/* Mask address off address to desired size. */
76 	if (addr_netmask(addr->af, bits, &xmask) != 0 ||
77 	    addr_and(masked, addr, &xmask) != 0) {
78 		debug3_f("%s: invalid mask %d bits", __func__, bits);
79 		return -1;
80 	}
81 	return 0;
82 }
83 
84 static int
srclimit_peer_addr(int sock,struct xaddr * addr)85 srclimit_peer_addr(int sock, struct xaddr *addr)
86 {
87 	struct sockaddr_storage storage;
88 	socklen_t addrlen = sizeof(storage);
89 	struct sockaddr *sa = (struct sockaddr *)&storage;
90 
91 	if (getpeername(sock, sa, &addrlen) != 0)
92 		return 1;	/* not remote socket? */
93 	if (addr_sa_to_xaddr(sa, addrlen, addr) != 0)
94 		return 1;	/* unknown address family? */
95 	return 0;
96 }
97 
98 void
srclimit_init(int max,int persource,int ipv4len,int ipv6len,struct per_source_penalty * penalty_conf,const char * penalty_exempt_conf)99 srclimit_init(int max, int persource, int ipv4len, int ipv6len,
100     struct per_source_penalty *penalty_conf, const char *penalty_exempt_conf)
101 {
102 	int i;
103 
104 	max_children = max;
105 	ipv4_masklen = ipv4len;
106 	ipv6_masklen = ipv6len;
107 	max_persource = persource;
108 	penalty_cfg = *penalty_conf;
109 	if (penalty_cfg.max_sources4 < 0 || penalty_cfg.max_sources6 < 0)
110 		fatal_f("invalid max_sources"); /* shouldn't happen */
111 	penalty_exempt = penalty_exempt_conf == NULL ?
112 	    NULL : xstrdup(penalty_exempt_conf);
113 	RB_INIT(&penalties_by_addr4);
114 	RB_INIT(&penalties_by_expiry4);
115 	RB_INIT(&penalties_by_addr6);
116 	RB_INIT(&penalties_by_expiry6);
117 	if (max_persource == INT_MAX)	/* no limit */
118 		return;
119 	debug("%s: max connections %d, per source %d, masks %d,%d", __func__,
120 	    max, persource, ipv4len, ipv6len);
121 	if (max <= 0)
122 		fatal("%s: invalid number of sockets: %d", __func__, max);
123 	children = xcalloc(max_children, sizeof(*children));
124 	for (i = 0; i < max_children; i++)
125 		children[i].id = -1;
126 }
127 
128 /* returns 1 if connection allowed, 0 if not allowed. */
129 int
srclimit_check_allow(int sock,int id)130 srclimit_check_allow(int sock, int id)
131 {
132 	struct xaddr xa, xb;
133 	int i, bits, first_unused, count = 0;
134 	char xas[NI_MAXHOST];
135 
136 	if (max_persource == INT_MAX)	/* no limit */
137 		return 1;
138 
139 	debug("%s: sock %d id %d limit %d", __func__, sock, id, max_persource);
140 	if (srclimit_peer_addr(sock, &xa) != 0)
141 		return 1;
142 	bits = xa.af == AF_INET ? ipv4_masklen : ipv6_masklen;
143 	if (srclimit_mask_addr(&xa, bits, &xb) != 0)
144 		return 1;
145 
146 	first_unused = max_children;
147 	/* Count matching entries and find first unused one. */
148 	for (i = 0; i < max_children; i++) {
149 		if (children[i].id == -1) {
150 			if (i < first_unused)
151 				first_unused = i;
152 		} else if (addr_cmp(&children[i].addr, &xb) == 0) {
153 			count++;
154 		}
155 	}
156 	if (addr_ntop(&xa, xas, sizeof(xas)) != 0) {
157 		debug3("%s: addr ntop failed", __func__);
158 		return 1;
159 	}
160 	debug3("%s: new unauthenticated connection from %s/%d, at %d of %d",
161 	    __func__, xas, bits, count, max_persource);
162 
163 	if (first_unused == max_children) { /* no free slot found */
164 		debug3("%s: no free slot", __func__);
165 		return 0;
166 	}
167 	if (first_unused < 0 || first_unused >= max_children)
168 		fatal("%s: internal error: first_unused out of range",
169 		    __func__);
170 
171 	if (count >= max_persource)
172 		return 0;
173 
174 	/* Connection allowed, store masked address. */
175 	children[first_unused].id = id;
176 	memcpy(&children[first_unused].addr, &xb, sizeof(xb));
177 	return 1;
178 }
179 
180 void
srclimit_done(int id)181 srclimit_done(int id)
182 {
183 	int i;
184 
185 	if (max_persource == INT_MAX)	/* no limit */
186 		return;
187 
188 	debug("%s: id %d", __func__, id);
189 	/* Clear corresponding state entry. */
190 	for (i = 0; i < max_children; i++) {
191 		if (children[i].id == id) {
192 			children[i].id = -1;
193 			return;
194 		}
195 	}
196 }
197 
198 static int
penalty_addr_cmp(struct penalty * a,struct penalty * b)199 penalty_addr_cmp(struct penalty *a, struct penalty *b)
200 {
201 	return addr_cmp(&a->addr, &b->addr);
202 	/* Addresses must be unique in by_addr, so no need to tiebreak */
203 }
204 
205 static int
penalty_expiry_cmp(struct penalty * a,struct penalty * b)206 penalty_expiry_cmp(struct penalty *a, struct penalty *b)
207 {
208 	if (a->expiry != b->expiry)
209 		return a->expiry < b->expiry ? -1 : 1;
210 	/* Tiebreak on addresses */
211 	return addr_cmp(&a->addr, &b->addr);
212 }
213 
214 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)215 expire_penalties_from_tree(time_t now, const char *t,
216     struct penalties_by_expiry *by_expiry,
217     struct penalties_by_addr *by_addr, size_t *npenaltiesp)
218 {
219 	struct penalty *penalty, *tmp;
220 
221 	/* XXX avoid full scan of tree, e.g. min-heap */
222 	RB_FOREACH_SAFE(penalty, penalties_by_expiry, by_expiry, tmp) {
223 		if (penalty->expiry >= now)
224 			break;
225 		if (RB_REMOVE(penalties_by_expiry, by_expiry,
226 		    penalty) != penalty ||
227 		    RB_REMOVE(penalties_by_addr, by_addr,
228 		    penalty) != penalty)
229 			fatal_f("internal error: %s penalty table corrupt", t);
230 		free(penalty);
231 		if ((*npenaltiesp)-- == 0)
232 			fatal_f("internal error: %s npenalties underflow", t);
233 	}
234 }
235 
236 static void
expire_penalties(time_t now)237 expire_penalties(time_t now)
238 {
239 	expire_penalties_from_tree(now, "ipv4",
240 	    &penalties_by_expiry4, &penalties_by_addr4, &npenalties4);
241 	expire_penalties_from_tree(now, "ipv6",
242 	    &penalties_by_expiry6, &penalties_by_addr6, &npenalties6);
243 }
244 
245 static void
addr_masklen_ntop(struct xaddr * addr,int masklen,char * s,size_t slen)246 addr_masklen_ntop(struct xaddr *addr, int masklen, char *s, size_t slen)
247 {
248 	size_t o;
249 
250 	if (addr_ntop(addr, s, slen) != 0) {
251 		strlcpy(s, "UNKNOWN", slen);
252 		return;
253 	}
254 	if ((o = strlen(s)) < slen)
255 		snprintf(s + o, slen - o, "/%d", masklen);
256 }
257 
258 int
srclimit_penalty_check_allow(int sock,const char ** reason)259 srclimit_penalty_check_allow(int sock, const char **reason)
260 {
261 	struct xaddr addr;
262 	struct penalty find, *penalty;
263 	time_t now;
264 	int bits, max_sources, overflow_mode;
265 	char addr_s[NI_MAXHOST];
266 	struct penalties_by_addr *by_addr;
267 	size_t npenalties;
268 
269 	if (!penalty_cfg.enabled)
270 		return 1;
271 	if (srclimit_peer_addr(sock, &addr) != 0)
272 		return 1;
273 	if (penalty_exempt != NULL) {
274 		if (addr_ntop(&addr, addr_s, sizeof(addr_s)) != 0)
275 			return 1; /* shouldn't happen */
276 		if (addr_match_list(addr_s, penalty_exempt) == 1) {
277 			return 1;
278 		}
279 	}
280 	now = monotime();
281 	expire_penalties(now);
282 	by_addr = addr.af == AF_INET ?
283 	    &penalties_by_addr4 : &penalties_by_addr6;
284 	max_sources = addr.af == AF_INET ?
285 	    penalty_cfg.max_sources4 : penalty_cfg.max_sources6;
286 	overflow_mode = addr.af == AF_INET ?
287 	    penalty_cfg.overflow_mode : penalty_cfg.overflow_mode6;
288 	npenalties = addr.af == AF_INET ?  npenalties4 : npenalties6;
289 	if (npenalties >= (size_t)max_sources &&
290 	    overflow_mode == PER_SOURCE_PENALTY_OVERFLOW_DENY_ALL) {
291 		*reason = "too many penalised addresses";
292 		return 0;
293 	}
294 	bits = addr.af == AF_INET ? ipv4_masklen : ipv6_masklen;
295 	memset(&find, 0, sizeof(find));
296 	if (srclimit_mask_addr(&addr, bits, &find.addr) != 0)
297 		return 1;
298 	if ((penalty = RB_FIND(penalties_by_addr, by_addr, &find)) == NULL)
299 		return 1; /* no penalty */
300 	if (penalty->expiry < now) {
301 		expire_penalties(now);
302 		return 1; /* expired penalty */
303 	}
304 	if (!penalty->active)
305 		return 1; /* Penalty hasn't hit activation threshold yet */
306 	*reason = penalty->reason;
307 	return 0;
308 }
309 
310 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)311 srclimit_early_expire_penalties_from_tree(const char *t,
312     struct penalties_by_expiry *by_expiry,
313     struct penalties_by_addr *by_addr, size_t *npenaltiesp, size_t max_sources)
314 {
315 	struct penalty *p = NULL;
316 	int bits;
317 	char s[NI_MAXHOST + 4];
318 
319 	/* Delete the soonest-to-expire penalties. */
320 	while (*npenaltiesp > max_sources) {
321 		if ((p = RB_MIN(penalties_by_expiry, by_expiry)) == NULL)
322 			fatal_f("internal error: %s table corrupt (find)", t);
323 		bits = p->addr.af == AF_INET ? ipv4_masklen : ipv6_masklen;
324 		addr_masklen_ntop(&p->addr, bits, s, sizeof(s));
325 		debug3_f("%s overflow, remove %s", t, s);
326 		if (RB_REMOVE(penalties_by_expiry, by_expiry, p) != p ||
327 		    RB_REMOVE(penalties_by_addr, by_addr, p) != p)
328 			fatal_f("internal error: %s table corrupt (remove)", t);
329 		free(p);
330 		(*npenaltiesp)--;
331 	}
332 }
333 
334 static void
srclimit_early_expire_penalties(void)335 srclimit_early_expire_penalties(void)
336 {
337 	srclimit_early_expire_penalties_from_tree("ipv4",
338 	    &penalties_by_expiry4, &penalties_by_addr4, &npenalties4,
339 	    (size_t)penalty_cfg.max_sources4);
340 	srclimit_early_expire_penalties_from_tree("ipv6",
341 	    &penalties_by_expiry6, &penalties_by_addr6, &npenalties6,
342 	    (size_t)penalty_cfg.max_sources6);
343 }
344 
345 void
srclimit_penalise(struct xaddr * addr,int penalty_type)346 srclimit_penalise(struct xaddr *addr, int penalty_type)
347 {
348 	struct xaddr masked;
349 	struct penalty *penalty = NULL, *existing = NULL;
350 	time_t now;
351 	int bits, penalty_secs, max_sources = 0, overflow_mode;
352 	char addrnetmask[NI_MAXHOST + 4];
353 	const char *reason = NULL, *t;
354 	size_t *npenaltiesp = NULL;
355 	struct penalties_by_addr *by_addr = NULL;
356 	struct penalties_by_expiry *by_expiry = NULL;
357 
358 	if (!penalty_cfg.enabled)
359 		return;
360 	if (penalty_exempt != NULL) {
361 		if (addr_ntop(addr, addrnetmask, sizeof(addrnetmask)) != 0)
362 			return; /* shouldn't happen */
363 		if (addr_match_list(addrnetmask, penalty_exempt) == 1) {
364 			debug3_f("address %s is exempt", addrnetmask);
365 			return;
366 		}
367 	}
368 
369 	switch (penalty_type) {
370 	case SRCLIMIT_PENALTY_NONE:
371 		return;
372 	case SRCLIMIT_PENALTY_CRASH:
373 		penalty_secs = penalty_cfg.penalty_crash;
374 		reason = "penalty: caused crash";
375 		break;
376 	case SRCLIMIT_PENALTY_AUTHFAIL:
377 		penalty_secs = penalty_cfg.penalty_authfail;
378 		reason = "penalty: failed authentication";
379 		break;
380 	case SRCLIMIT_PENALTY_NOAUTH:
381 		penalty_secs = penalty_cfg.penalty_noauth;
382 		reason = "penalty: connections without attempting authentication";
383 		break;
384 	case SRCLIMIT_PENALTY_REFUSECONNECTION:
385 		penalty_secs = penalty_cfg.penalty_refuseconnection;
386 		reason = "penalty: connection prohibited by RefuseConnection";
387 		break;
388 	case SRCLIMIT_PENALTY_GRACE_EXCEEDED:
389 		penalty_secs = penalty_cfg.penalty_crash;
390 		reason = "penalty: exceeded LoginGraceTime";
391 		break;
392 	default:
393 		fatal_f("internal error: unknown penalty %d", penalty_type);
394 	}
395 	bits = addr->af == AF_INET ? ipv4_masklen : ipv6_masklen;
396 	if (srclimit_mask_addr(addr, bits, &masked) != 0)
397 		return;
398 	addr_masklen_ntop(addr, bits, addrnetmask, sizeof(addrnetmask));
399 
400 	now = monotime();
401 	expire_penalties(now);
402 	by_expiry = addr->af == AF_INET ?
403 	    &penalties_by_expiry4 : &penalties_by_expiry6;
404 	by_addr = addr->af == AF_INET ?
405 	    &penalties_by_addr4 : &penalties_by_addr6;
406 	max_sources = addr->af == AF_INET ?
407 	    penalty_cfg.max_sources4 : penalty_cfg.max_sources6;
408 	overflow_mode = addr->af == AF_INET ?
409 	    penalty_cfg.overflow_mode : penalty_cfg.overflow_mode6;
410 	npenaltiesp = addr->af == AF_INET ?  &npenalties4 : &npenalties6;
411 	t = addr->af == AF_INET ? "ipv4" : "ipv6";
412 	if (*npenaltiesp >= (size_t)max_sources &&
413 	    overflow_mode == PER_SOURCE_PENALTY_OVERFLOW_DENY_ALL) {
414 		verbose_f("%s penalty table full, cannot penalise %s for %s", t,
415 		    addrnetmask, reason);
416 		return;
417 	}
418 
419 	penalty = xcalloc(1, sizeof(*penalty));
420 	penalty->addr = masked;
421 	penalty->expiry = now + penalty_secs;
422 	penalty->reason = reason;
423 	if ((existing = RB_INSERT(penalties_by_addr, by_addr,
424 	    penalty)) == NULL) {
425 		/* penalty didn't previously exist */
426 		if (penalty_secs > penalty_cfg.penalty_min)
427 			penalty->active = 1;
428 		if (RB_INSERT(penalties_by_expiry, by_expiry, penalty) != NULL)
429 			fatal_f("internal error: %s penalty tables corrupt", t);
430 		verbose_f("%s: new %s %s penalty of %d seconds for %s", t,
431 		    addrnetmask, penalty->active ? "active" : "deferred",
432 		    penalty_secs, reason);
433 		if (++(*npenaltiesp) > (size_t)max_sources)
434 			srclimit_early_expire_penalties(); /* permissive */
435 		return;
436 	}
437 	debug_f("%s penalty for %s %s already exists, %lld seconds remaining",
438 	    existing->active ? "active" : "inactive", t,
439 	    addrnetmask, (long long)(existing->expiry - now));
440 	/* Expiry information is about to change, remove from tree */
441 	if (RB_REMOVE(penalties_by_expiry, by_expiry, existing) != existing)
442 		fatal_f("internal error: %s penalty table corrupt (remove)", t);
443 	/* An entry already existed. Accumulate penalty up to maximum */
444 	existing->expiry += penalty_secs;
445 	if (existing->expiry - now > penalty_cfg.penalty_max)
446 		existing->expiry = now + penalty_cfg.penalty_max;
447 	if (existing->expiry - now > penalty_cfg.penalty_min &&
448 	    !existing->active) {
449 		verbose_f("%s: activating %s penalty of %lld seconds for %s",
450 		    addrnetmask, t, (long long)(existing->expiry - now),
451 		    reason);
452 		existing->active = 1;
453 	}
454 	existing->reason = penalty->reason;
455 	free(penalty);
456 	penalty = NULL;
457 	/* Re-insert into expiry tree */
458 	if (RB_INSERT(penalties_by_expiry, by_expiry, existing) != NULL)
459 		fatal_f("internal error: %s penalty table corrupt (insert)", t);
460 }
461 
462 static void
srclimit_penalty_info_for_tree(const char * t,struct penalties_by_expiry * by_expiry,size_t npenalties)463 srclimit_penalty_info_for_tree(const char *t,
464     struct penalties_by_expiry *by_expiry, size_t npenalties)
465 {
466 	struct penalty *p = NULL;
467 	int bits;
468 	char s[NI_MAXHOST + 4];
469 	time_t now;
470 
471 	now = monotime();
472 	logit("%zu active %s penalties", npenalties, t);
473 	RB_FOREACH(p, penalties_by_expiry, by_expiry) {
474 		bits = p->addr.af == AF_INET ? ipv4_masklen : ipv6_masklen;
475 		addr_masklen_ntop(&p->addr, bits, s, sizeof(s));
476 		if (p->expiry < now)
477 			logit("client %s %s (expired)", s, p->reason);
478 		else {
479 			logit("client %s %s (%llu secs left)", s, p->reason,
480 			   (long long)(p->expiry - now));
481 		}
482 	}
483 }
484 
485 void
srclimit_penalty_info(void)486 srclimit_penalty_info(void)
487 {
488 	srclimit_penalty_info_for_tree("ipv4",
489 	    &penalties_by_expiry4, npenalties4);
490 	srclimit_penalty_info_for_tree("ipv6",
491 	    &penalties_by_expiry6, npenalties6);
492 }
493