xref: /dflybsd-src/crypto/openssh/srclimit.c (revision 94803e438e74ac6f056ac8f81e98b53d69440f08)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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_GRACE_EXCEEDED:
385 		penalty_secs = penalty_cfg.penalty_crash;
386 		reason = "penalty: exceeded LoginGraceTime";
387 		break;
388 	default:
389 		fatal_f("internal error: unknown penalty %d", penalty_type);
390 	}
391 	bits = addr->af == AF_INET ? ipv4_masklen : ipv6_masklen;
392 	if (srclimit_mask_addr(addr, bits, &masked) != 0)
393 		return;
394 	addr_masklen_ntop(addr, bits, addrnetmask, sizeof(addrnetmask));
395 
396 	now = monotime();
397 	expire_penalties(now);
398 	by_expiry = addr->af == AF_INET ?
399 	    &penalties_by_expiry4 : &penalties_by_expiry6;
400 	by_addr = addr->af == AF_INET ?
401 	    &penalties_by_addr4 : &penalties_by_addr6;
402 	max_sources = addr->af == AF_INET ?
403 	    penalty_cfg.max_sources4 : penalty_cfg.max_sources6;
404 	overflow_mode = addr->af == AF_INET ?
405 	    penalty_cfg.overflow_mode : penalty_cfg.overflow_mode6;
406 	npenaltiesp = addr->af == AF_INET ?  &npenalties4 : &npenalties6;
407 	t = addr->af == AF_INET ? "ipv4" : "ipv6";
408 	if (*npenaltiesp >= (size_t)max_sources &&
409 	    overflow_mode == PER_SOURCE_PENALTY_OVERFLOW_DENY_ALL) {
410 		verbose_f("%s penalty table full, cannot penalise %s for %s", t,
411 		    addrnetmask, reason);
412 		return;
413 	}
414 
415 	penalty = xcalloc(1, sizeof(*penalty));
416 	penalty->addr = masked;
417 	penalty->expiry = now + penalty_secs;
418 	penalty->reason = reason;
419 	if ((existing = RB_INSERT(penalties_by_addr, by_addr,
420 	    penalty)) == NULL) {
421 		/* penalty didn't previously exist */
422 		if (penalty_secs > penalty_cfg.penalty_min)
423 			penalty->active = 1;
424 		if (RB_INSERT(penalties_by_expiry, by_expiry, penalty) != NULL)
425 			fatal_f("internal error: %s penalty tables corrupt", t);
426 		verbose_f("%s: new %s %s penalty of %d seconds for %s", t,
427 		    addrnetmask, penalty->active ? "active" : "deferred",
428 		    penalty_secs, reason);
429 		if (++(*npenaltiesp) > (size_t)max_sources)
430 			srclimit_early_expire_penalties(); /* permissive */
431 		return;
432 	}
433 	debug_f("%s penalty for %s %s already exists, %lld seconds remaining",
434 	    existing->active ? "active" : "inactive", t,
435 	    addrnetmask, (long long)(existing->expiry - now));
436 	/* Expiry information is about to change, remove from tree */
437 	if (RB_REMOVE(penalties_by_expiry, by_expiry, existing) != existing)
438 		fatal_f("internal error: %s penalty table corrupt (remove)", t);
439 	/* An entry already existed. Accumulate penalty up to maximum */
440 	existing->expiry += penalty_secs;
441 	if (existing->expiry - now > penalty_cfg.penalty_max)
442 		existing->expiry = now + penalty_cfg.penalty_max;
443 	if (existing->expiry - now > penalty_cfg.penalty_min &&
444 	    !existing->active) {
445 		verbose_f("%s: activating %s penalty of %lld seconds for %s",
446 		    addrnetmask, t, (long long)(existing->expiry - now),
447 		    reason);
448 		existing->active = 1;
449 	}
450 	existing->reason = penalty->reason;
451 	free(penalty);
452 	penalty = NULL;
453 	/* Re-insert into expiry tree */
454 	if (RB_INSERT(penalties_by_expiry, by_expiry, existing) != NULL)
455 		fatal_f("internal error: %s penalty table corrupt (insert)", t);
456 }
457 
458 static void
459 srclimit_penalty_info_for_tree(const char *t,
460     struct penalties_by_expiry *by_expiry, size_t npenalties)
461 {
462 	struct penalty *p = NULL;
463 	int bits;
464 	char s[NI_MAXHOST + 4];
465 	time_t now;
466 
467 	now = monotime();
468 	logit("%zu active %s penalties", npenalties, t);
469 	RB_FOREACH(p, penalties_by_expiry, by_expiry) {
470 		bits = p->addr.af == AF_INET ? ipv4_masklen : ipv6_masklen;
471 		addr_masklen_ntop(&p->addr, bits, s, sizeof(s));
472 		if (p->expiry < now)
473 			logit("client %s %s (expired)", s, p->reason);
474 		else {
475 			logit("client %s %s (%llu secs left)", s, p->reason,
476 			   (long long)(p->expiry - now));
477 		}
478 	}
479 }
480 
481 void
482 srclimit_penalty_info(void)
483 {
484 	srclimit_penalty_info_for_tree("ipv4",
485 	    &penalties_by_expiry4, npenalties4);
486 	srclimit_penalty_info_for_tree("ipv6",
487 	    &penalties_by_expiry6, npenalties6);
488 }
489