xref: /openbsd/libexec/spamd/sdl.c (revision 6d9ddefd)
1 /*	$OpenBSD: sdl.c,v 1.25 2022/12/26 20:06:43 jmc Exp $ */
2 
3 /*
4  * Copyright (c) 2003-2007 Bob Beck.  All rights reserved.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * sdl.c - Implement spamd source lists
21  *
22  * This consists of everything we need to do to determine which lists
23  * someone is on. Spamd gets the connecting address, and looks it up
24  * against all lists to determine what deferral messages to feed back
25  * to the connecting machine. - The redirection to spamd will happen
26  * from pf in the kernel, first match will divert to us. Spamd (along with
27  * setup) must keep track of *all* matches, so as to tell someone all the
28  * lists that they are on.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include "sdl.h"
40 
41 static void sdl_free(struct sdlist *);
42 static void sdl_clear(struct sdlist *);
43 
44 extern int debug;
45 struct sdlist *blacklists = NULL;
46 int blc = 0, blu = 0;
47 
48 static int
compar_v4(const void * va,const void * vb)49 compar_v4(const void *va, const void *vb)
50 {
51 	const struct sdentry_v4 *a = va;
52 	const struct sdentry_v4 *b = vb;
53 	struct in_addr aa;
54 	struct in_addr bb;
55 
56 	/* The mask has already been applied. */
57 	aa.s_addr = ntohl(a->sda.s_addr);
58 	bb.s_addr = ntohl(b->sda.s_addr);
59 
60 	if (aa.s_addr > bb.s_addr)
61 		return (1);
62 	if (aa.s_addr < bb.s_addr)
63 		return (-1);
64 	return (0);
65 }
66 
67 static int
compar_v6(const void * va,const void * vb)68 compar_v6(const void *va, const void *vb)
69 {
70 	const struct sdentry_v6 *a = va;
71 	const struct sdentry_v6 *b = vb;
72 	struct sdaddr_v6 aa;
73 	struct sdaddr_v6 bb;
74 
75 	/* The mask has already been applied. */
76 	aa.addr32[0] = ntohl(a->sda.addr32[0]);
77 	aa.addr32[1] = ntohl(a->sda.addr32[1]);
78 	aa.addr32[2] = ntohl(a->sda.addr32[2]);
79 	aa.addr32[3] = ntohl(a->sda.addr32[3]);
80 
81 	bb.addr32[0] = ntohl(b->sda.addr32[0]);
82 	bb.addr32[1] = ntohl(b->sda.addr32[1]);
83 	bb.addr32[2] = ntohl(b->sda.addr32[2]);
84 	bb.addr32[3] = ntohl(b->sda.addr32[3]);
85 
86 	if (aa.addr32[0] > bb.addr32[0])
87 		return (1);
88 	if (aa.addr32[0] < bb.addr32[0])
89 		return (-1);
90 	if (aa.addr32[1] > bb.addr32[1])
91 		return (1);
92 	if (aa.addr32[1] < bb.addr32[1])
93 		return (-1);
94 	if (aa.addr32[2] > bb.addr32[2])
95 		return (1);
96 	if (aa.addr32[2] < bb.addr32[2])
97 		return (-1);
98 	if (aa.addr32[3] > bb.addr32[3])
99 		return (1);
100 	if (aa.addr32[3] < bb.addr32[3])
101 		return (-1);
102 	return (0);
103 }
104 
105 int
sdl_add(char * sdname,char * sdstring,char ** v4,u_int nv4,char ** v6,u_int nv6)106 sdl_add(char *sdname, char *sdstring, char **v4, u_int nv4, char **v6, u_int nv6)
107 {
108 	int i, idx = -1;
109 	char astring[40];
110 	char *addr = NULL;
111 	unsigned int maskbits;
112 
113 	/*
114 	 * if a blacklist of same tag name is already there, replace it,
115 	 * otherwise append.
116 	 */
117 	for (i = 0; i < blu; i++) {
118 		if (strcmp(blacklists[i].tag, sdname) == 0) {
119 			idx = i;
120 			break;
121 		}
122 	}
123 	if (idx != -1) {
124 		if (debug > 0)
125 			printf("replacing list %s; %u new entries\n",
126 			    blacklists[idx].tag, nv4 + nv6);
127 		sdl_free(&blacklists[idx]);
128 	} else {
129 		if (debug > 0)
130 			printf("adding list %s; %u entries\n", sdname, nv4 + nv6);
131 		if (blu == blc) {
132 			struct sdlist *tmp;
133 
134 			tmp = reallocarray(blacklists, blc + 128,
135 			    sizeof(struct sdlist));
136 			if (tmp == NULL)
137 				return (-1);
138 			blacklists = tmp;
139 			blc += 128;
140 			sdl_clear(&blacklists[blu]);
141 		}
142 		idx = blu;
143 	}
144 
145 	if ((blacklists[idx].tag = strdup(sdname)) == NULL)
146 		goto misc_error;
147 	if ((blacklists[idx].string = strdup(sdstring)) == NULL)
148 		goto misc_error;
149 
150 	/*
151 	 * Cycle through addrs by family, converting. We assume they are
152 	 * correctly formatted v4 and v6 addrs, if they don't all convert
153 	 * correctly, the add fails. Each address should be address/maskbits.
154 	 */
155 	if (nv4 != 0) {
156 		blacklists[idx].v4.naddrs = nv4;
157 		blacklists[idx].v4.addrs = reallocarray(NULL, nv4,
158 		    sizeof(struct sdentry_v4));
159 		if (blacklists[idx].v4.addrs == NULL)
160 			goto misc_error;
161 		for (i = 0; i < nv4; i++) {
162 			struct in_addr *m, *n;
163 			int j;
164 
165 			n = &blacklists[idx].v4.addrs[i].sda;
166 			m = &blacklists[idx].v4.addrs[i].sdm;
167 
168 			addr = v4[i];
169 			j = sscanf(addr, "%15[^/]/%u", astring, &maskbits);
170 			if (j != 2)
171 				goto parse_error;
172 			/*
173 			 * sanity check! we don't allow a 0 mask -
174 			 * don't blacklist the entire net.
175 			 */
176 			if (maskbits == 0 || maskbits > 32)
177 				goto parse_error;
178 			j = inet_pton(AF_INET, astring, n);
179 			if (j != 1)
180 				goto parse_error;
181 			if (debug > 0)
182 				printf("added %s/%u\n", astring, maskbits);
183 
184 			/* set mask. */
185 			m->s_addr = 0xffffffffU << (32 - maskbits);
186 			m->s_addr = htonl(m->s_addr);
187 
188 			/* mask off address bits that won't ever be used */
189 			n->s_addr = n->s_addr & m->s_addr;
190 		}
191 		/* spamd-setup output is sorted in host byte order */
192 		mergesort(blacklists[idx].v4.addrs, nv4,
193 		    sizeof(struct sdentry_v4), compar_v4);
194 	}
195 	if (nv6 != 0) {
196 		blacklists[idx].v6.naddrs = nv6;
197 		blacklists[idx].v6.addrs = reallocarray(NULL, nv6,
198 		    sizeof(struct sdentry_v6));
199 		if (blacklists[idx].v6.addrs == NULL)
200 			goto misc_error;
201 
202 		for (i = 0; i < nv6; i++) {
203 			int j, k;
204 			struct sdaddr_v6 *m, *n;
205 
206 			n = &blacklists[idx].v6.addrs[i].sda;
207 			m = &blacklists[idx].v6.addrs[i].sdm;
208 
209 			addr = v6[i];
210 			j = sscanf(addr, "%39[^/]/%u", astring, &maskbits);
211 			if (j != 2)
212 				goto parse_error;
213 			/*
214 			 * sanity check! we don't allow a 0 mask -
215 			 * don't blacklist the entire net.
216 			 */
217 			if (maskbits == 0 || maskbits > 128)
218 				goto parse_error;
219 			j = inet_pton(AF_INET6, astring, n);
220 			if (j != 1)
221 				goto parse_error;
222 			if (debug > 0)
223 				printf("added %s/%u\n", astring, maskbits);
224 
225 			/* set mask, borrowed from pf */
226 			k = 0;
227 			for (j = 0; j < 4; j++)
228 				m->addr32[j] = 0;
229 			while (maskbits >= 32) {
230 				m->addr32[k++] = 0xffffffffU;
231 				maskbits -= 32;
232 			}
233 			for (j = 31; j > 31 - maskbits; --j)
234 				m->addr32[k] |= (1 << j);
235 			if (maskbits)
236 				m->addr32[k] = htonl(m->addr32[k]);
237 
238 			/* mask off address bits that won't ever be used */
239 			for (j = 0; j < 4; j++)
240 				n->addr32[j] = n->addr32[j] & m->addr32[j];
241 		}
242 		/* spamd-setup output is sorted in host byte order */
243 		mergesort(blacklists[idx].v6.addrs, nv6,
244 		    sizeof(struct sdentry_v6), compar_v6);
245 	}
246 	if (idx == blu) {
247 		blu++;
248 		sdl_clear(&blacklists[blu]);
249 	}
250 	return (0);
251  parse_error:
252 	if (debug > 0)
253 		printf("sdl_add: parse error, \"%s\"\n", addr);
254  misc_error:
255 	sdl_free(&blacklists[idx]);
256 	if (idx != blu) {
257 		memmove(&blacklists[idx], &blacklists[idx + 1],
258 		    (blu - idx) * sizeof(*blacklists));
259 		blu--;
260 	}
261 	return (-1);
262 }
263 
264 void
sdl_del(char * sdname)265 sdl_del(char *sdname)
266 {
267 	int i, idx = -1;
268 
269 	for (i = 0; i < blu; i++) {
270 		if (strcmp(blacklists[i].tag, sdname) == 0) {
271 			idx = i;
272 			break;
273 		}
274 	}
275 	if (idx != -1) {
276 		if (debug > 0)
277 			printf("clearing list %s\n", sdname);
278 		/* Must preserve tag. */
279 		free(blacklists[idx].string);
280 		free(blacklists[idx].v4.addrs);
281 		free(blacklists[idx].v6.addrs);
282 		blacklists[idx].string = NULL;
283 		blacklists[idx].v4.addrs = NULL;
284 		blacklists[idx].v6.addrs = NULL;
285 		blacklists[idx].v4.naddrs = 0;
286 		blacklists[idx].v6.naddrs = 0;
287 	}
288 }
289 
290 /*
291  * Return 0 if the address a (with mask m) matches address key
292  * otherwise return 1 if a > key or -1 if a < key.  It is assumed
293  * that address a has been pre-masked out, we only need to mask key.
294  */
295 static int
match_addr_v4(const void * vkey,const void * ventry)296 match_addr_v4(const void *vkey, const void *ventry)
297 {
298 	const struct in_addr *k = vkey;
299 	const struct in_addr *a = &((const struct sdentry_v4 *)ventry)->sda;
300 	const struct in_addr *m = &((const struct sdentry_v4 *)ventry)->sdm;
301 	struct in_addr kk;
302 	struct in_addr aa;
303 
304 	kk.s_addr = ntohl(k->s_addr & m->s_addr);
305 	aa.s_addr = ntohl(a->s_addr);
306 	if (kk.s_addr > aa.s_addr)
307 		return (1);
308 	if (kk.s_addr < aa.s_addr)
309 		return (-1);
310 	return (0);
311 }
312 
313 /*
314  * Return 0 if the address a (with mask m) matches address key
315  * otherwise return 1 if a > key or -1 if a < key.  It is assumed
316  * that address a has been pre-masked out, we only need to mask key.
317  */
318 static int
match_addr_v6(const void * vkey,const void * ventry)319 match_addr_v6(const void *vkey, const void *ventry)
320 {
321 	const struct sdaddr_v6 *k = vkey;
322 	const struct sdaddr_v6 *a = &((const struct sdentry_v6 *)ventry)->sda;
323 	const struct sdaddr_v6 *m = &((const struct sdentry_v6 *)ventry)->sdm;
324 	struct sdaddr_v6 kk;
325 	struct sdaddr_v6 aa;
326 
327 	kk.addr32[0] = ntohl(k->addr32[0] & m->addr32[0]);
328 	kk.addr32[1] = ntohl(k->addr32[1] & m->addr32[1]);
329 	kk.addr32[2] = ntohl(k->addr32[2] & m->addr32[2]);
330 	kk.addr32[3] = ntohl(k->addr32[3] & m->addr32[3]);
331 
332 	aa.addr32[0] = ntohl(a->addr32[0]);
333 	aa.addr32[1] = ntohl(a->addr32[1]);
334 	aa.addr32[2] = ntohl(a->addr32[2]);
335 	aa.addr32[3] = ntohl(a->addr32[3]);
336 
337 	if (kk.addr32[0] > aa.addr32[0])
338 		return (1);
339 	if (kk.addr32[0] < aa.addr32[0])
340 		return (-1);
341 	if (kk.addr32[1] > aa.addr32[1])
342 		return (1);
343 	if (kk.addr32[1] < aa.addr32[1])
344 		return (-1);
345 	if (kk.addr32[2] > aa.addr32[2])
346 		return (1);
347 	if (kk.addr32[2] < aa.addr32[2])
348 		return (-1);
349 	if (kk.addr32[3] > aa.addr32[3])
350 		return (1);
351 	if (kk.addr32[3] < aa.addr32[3])
352 		return (-1);
353 	return (0);
354 }
355 
356 #define grow_sdlist(sd, c, l) do {					       \
357 	if (c == l) {							       \
358 		struct sdlist **tmp;					       \
359 									       \
360 		tmp = reallocarray(sd, l + 128, sizeof(struct sdlist *));      \
361 		if (tmp == NULL) {					       \
362 			/*						       \
363 			 * XXX out of memory - return what we have	       \
364 			 */						       \
365 			return (sdnew);					       \
366 		}							       \
367 		sd = tmp;						       \
368 		l += 128;						       \
369 	}								       \
370 } while (0)
371 
372 static struct sdlist **
sdl_lookup_v4(struct sdlist * sdl,struct in_addr * src)373 sdl_lookup_v4(struct sdlist *sdl, struct in_addr *src)
374 {
375 	int matches = 0;
376 	int sdnewlen = 0;
377 	struct sdlist **sdnew = NULL;
378 
379 	while (sdl->tag != NULL) {
380 		if (bsearch(src, sdl->v4.addrs, sdl->v4.naddrs,
381 		    sizeof(struct sdentry_v4), match_addr_v4) != NULL) {
382 			grow_sdlist(sdnew, matches, sdnewlen);
383 			sdnew[matches] = sdl;
384 			matches++;
385 			sdnew[matches] = NULL;
386 			break;
387 		}
388 		sdl++;
389 	}
390 	return (sdnew);
391 }
392 
393 static struct sdlist **
sdl_lookup_v6(struct sdlist * sdl,struct sdaddr_v6 * src)394 sdl_lookup_v6(struct sdlist *sdl, struct sdaddr_v6 *src)
395 {
396 	int matches = 0;
397 	int sdnewlen = 0;
398 	struct sdlist **sdnew = NULL;
399 
400 	while (sdl->tag != NULL) {
401 		if (bsearch(src, sdl->v6.addrs, sdl->v6.naddrs,
402 		    sizeof(struct sdentry_v6), match_addr_v6) != NULL) {
403 			grow_sdlist(sdnew, matches, sdnewlen);
404 			sdnew[matches] = sdl;
405 			matches++;
406 			sdnew[matches] = NULL;
407 			break;
408 		}
409 		sdl++;
410 	}
411 	return (sdnew);
412 }
413 
414 /*
415  * Given an address and address family
416  * return list of pointers to matching nodes. or NULL if none.
417  */
418 struct sdlist **
sdl_lookup(struct sdlist * head,int af,void * src)419 sdl_lookup(struct sdlist *head, int af, void *src)
420 {
421 	if (head == NULL)
422 		return (NULL);
423 
424 	switch (af) {
425 	case AF_INET:
426 		return (sdl_lookup_v4(head, src));
427 	case AF_INET6:
428 		return (sdl_lookup_v6(head, src));
429 	default:
430 		return (NULL);
431 	}
432 }
433 
434 static int
sdl_check_v4(struct sdlist * sdl,struct in_addr * src)435 sdl_check_v4(struct sdlist *sdl, struct in_addr *src)
436 {
437 	while (sdl->tag != NULL) {
438 		if (bsearch(src, sdl->v4.addrs, sdl->v4.naddrs,
439 		    sizeof(struct sdentry_v4), match_addr_v4) != NULL)
440 			return (1);
441 		sdl++;
442 	}
443 	return (0);
444 }
445 
446 static int
sdl_check_v6(struct sdlist * sdl,struct sdaddr_v6 * src)447 sdl_check_v6(struct sdlist *sdl, struct sdaddr_v6 *src)
448 {
449 	while (sdl->tag != NULL) {
450 		if (bsearch(src, sdl->v6.addrs, sdl->v6.naddrs,
451 		    sizeof(struct sdentry_v6), match_addr_v6) != NULL)
452 			return (1);
453 		sdl++;
454 	}
455 	return (0);
456 }
457 
458 /*
459  * Given an address and address family
460  * returns 1 if address is on a blacklist, else 0.
461  */
462 int
sdl_check(struct sdlist * head,int af,void * src)463 sdl_check(struct sdlist *head, int af, void *src)
464 {
465 	if (head == NULL)
466 		return (0);
467 
468 	switch (af) {
469 	case AF_INET:
470 		return (sdl_check_v4(head, src));
471 	case AF_INET6:
472 		return (sdl_check_v6(head, src));
473 	default:
474 		return (0);
475 	}
476 }
477 
478 static void
sdl_free(struct sdlist * sdl)479 sdl_free(struct sdlist *sdl)
480 {
481 	free(sdl->tag);
482 	free(sdl->string);
483 	free(sdl->v4.addrs);
484 	free(sdl->v6.addrs);
485 	sdl_clear(sdl);
486 }
487 
488 static void
sdl_clear(struct sdlist * sdl)489 sdl_clear(struct sdlist *sdl)
490 {
491 	sdl->tag = NULL;
492 	sdl->string = NULL;
493 	sdl->v4.addrs = NULL;
494 	sdl->v4.naddrs = 0;
495 	sdl->v6.addrs = NULL;
496 	sdl->v6.naddrs = 0;
497 }
498