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
41static void sdl_free(struct sdlist *);
42static void sdl_clear(struct sdlist *);
43
44extern int debug;
45struct sdlist *blacklists = NULL;
46int blc = 0, blu = 0;
47
48static int
49compar_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
67static int
68compar_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
105int
106sdl_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
264void
265sdl_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 */
295static int
296match_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 */
318static int
319match_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
372static struct sdlist **
373sdl_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
393static struct sdlist **
394sdl_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 */
418struct sdlist **
419sdl_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
434static int
435sdl_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
446static int
447sdl_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 */
462int
463sdl_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
478static void
479sdl_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
488static void
489sdl_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