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