xref: /openbsd-src/libexec/spamd/sdl.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: sdl.c,v 1.22 2015/05/18 16:04:21 reyk 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 int
49 sdl_add(char *sdname, char *sdstring, char **v4, u_int nv4, char **v6, u_int nv6)
50 {
51 	int i, idx = -1;
52 	char astring[40];
53 	char *addr = NULL;
54 	unsigned int maskbits;
55 
56 	/*
57 	 * if a blacklist of same tag name is already there, replace it,
58 	 * otherwise append.
59 	 */
60 	for (i = 0; i < blu; i++) {
61 		if (strcmp(blacklists[i].tag, sdname) == 0) {
62 			idx = i;
63 			break;
64 		}
65 	}
66 	if (idx != -1) {
67 		if (debug > 0)
68 			printf("replacing list %s; %u new entries\n",
69 			    blacklists[idx].tag, nv4 + nv6);
70 		sdl_free(&blacklists[idx]);
71 	} else {
72 		if (debug > 0)
73 			printf("adding list %s; %u entries\n", sdname, nv4 + nv6);
74 		if (blu == blc) {
75 			struct sdlist *tmp;
76 
77 			tmp = reallocarray(blacklists, blc + 128,
78 			    sizeof(struct sdlist));
79 			if (tmp == NULL)
80 				return (-1);
81 			blacklists = tmp;
82 			blc += 128;
83 			sdl_clear(&blacklists[blu]);
84 		}
85 		idx = blu;
86 	}
87 
88 	if ((blacklists[idx].tag = strdup(sdname)) == NULL)
89 		goto misc_error;
90 	if ((blacklists[idx].string = strdup(sdstring)) == NULL)
91 		goto misc_error;
92 
93 	/*
94 	 * Cycle through addrs by family, converting. We assume they are
95 	 * correctly formatted v4 and v6 addrs, if they don't all convert
96 	 * correctly, the add fails. Each address should be address/maskbits.
97 	 */
98 	if (nv4 != 0) {
99 		blacklists[idx].v4.naddrs = nv4;
100 		blacklists[idx].v4.addrs = reallocarray(NULL, nv4,
101 		    sizeof(struct sdentry_v4));
102 		if (blacklists[idx].v4.addrs == NULL)
103 			goto misc_error;
104 		for (i = 0; i < nv4; i++) {
105 			struct in_addr *m, *n;
106 			int j;
107 
108 			n = &blacklists[idx].v4.addrs[i].sda;
109 			m = &blacklists[idx].v4.addrs[i].sdm;
110 
111 			addr = v4[i];
112 			j = sscanf(addr, "%15[^/]/%u", astring, &maskbits);
113 			if (j != 2)
114 				goto parse_error;
115 			/*
116 			 * sanity check! we don't allow a 0 mask -
117 			 * don't blacklist the entire net.
118 			 */
119 			if (maskbits == 0 || maskbits > 32)
120 				goto parse_error;
121 			j = inet_pton(AF_INET, astring, n);
122 			if (j != 1)
123 				goto parse_error;
124 			if (debug > 0)
125 				printf("added %s/%u\n", astring, maskbits);
126 
127 			/* set mask. */
128 			m->s_addr = 0xffffffffU << (32 - maskbits);
129 			m->s_addr = htonl(m->s_addr);
130 
131 			/* mask off address bits that won't ever be used */
132 			n->s_addr = n->s_addr & m->s_addr;
133 		}
134 	}
135 	if (nv6 != 0) {
136 		blacklists[idx].v6.naddrs = nv6;
137 		blacklists[idx].v6.addrs = reallocarray(NULL, nv6,
138 		    sizeof(struct sdentry_v6));
139 		if (blacklists[idx].v6.addrs == NULL)
140 			goto misc_error;
141 
142 		for (i = 0; i < nv6; i++) {
143 			int j, k;
144 			struct sdaddr_v6 *m, *n;
145 
146 			n = &blacklists[idx].v6.addrs[i].sda;
147 			m = &blacklists[idx].v6.addrs[i].sdm;
148 
149 			addr = v6[i];
150 			j = sscanf(addr, "%39[^/]/%u", astring, &maskbits);
151 			if (j != 2)
152 				goto parse_error;
153 			/*
154 			 * sanity check! we don't allow a 0 mask -
155 			 * don't blacklist the entire net.
156 			 */
157 			if (maskbits == 0 || maskbits > 128)
158 				goto parse_error;
159 			j = inet_pton(AF_INET6, astring, n);
160 			if (j != 1)
161 				goto parse_error;
162 			if (debug > 0)
163 				printf("added %s/%u\n", astring, maskbits);
164 
165 			/* set mask, borrowed from pf */
166 			k = 0;
167 			for (j = 0; j < 4; j++)
168 				m->addr32[j] = 0;
169 			while (maskbits >= 32) {
170 				m->addr32[k++] = 0xffffffffU;
171 				maskbits -= 32;
172 			}
173 			for (j = 31; j > 31 - maskbits; --j)
174 				m->addr32[k] |= (1 << j);
175 			if (maskbits)
176 				m->addr32[k] = htonl(m->addr32[k]);
177 
178 			/* mask off address bits that won't ever be used */
179 			for (j = 0; j < 4; j++)
180 				n->addr32[j] = n->addr32[j] & m->addr32[j];
181 		}
182 	}
183 	if (idx == blu) {
184 		blu++;
185 		sdl_clear(&blacklists[blu]);
186 	}
187 	return (0);
188  parse_error:
189 	if (debug > 0)
190 		printf("sdl_add: parse error, \"%s\"\n", addr);
191  misc_error:
192 	sdl_free(&blacklists[idx]);
193 	if (idx != blu) {
194 		memmove(&blacklists[idx], &blacklists[idx + 1],
195 		    (blu - idx) * sizeof(*blacklists));
196 		blu--;
197 	}
198 	return (-1);
199 }
200 
201 void
202 sdl_del(char *sdname)
203 {
204 	int i, idx = -1;
205 
206 	for (i = 0; i < blu; i++) {
207 		if (strcmp(blacklists[i].tag, sdname) == 0) {
208 			idx = i;
209 			break;
210 		}
211 	}
212 	if (idx != -1) {
213 		if (debug > 0)
214 			printf("clearing list %s\n", sdname);
215 		/* Must preserve tag. */
216 		free(blacklists[idx].string);
217 		free(blacklists[idx].v4.addrs);
218 		free(blacklists[idx].v6.addrs);
219 		blacklists[idx].string = NULL;
220 		blacklists[idx].v4.addrs = NULL;
221 		blacklists[idx].v6.addrs = NULL;
222 		blacklists[idx].v4.naddrs = 0;
223 		blacklists[idx].v6.naddrs = 0;
224 	}
225 }
226 
227 /*
228  * Return 1 if the addresses a (with mask m) matches address b
229  * otherwise return 0. It is assumed that address a has been
230  * pre-masked out, we only need to mask b.
231  */
232 static int
233 match_addr_v4(struct in_addr *a, struct in_addr *m, struct in_addr *b)
234 {
235 	if (a->s_addr == (b->s_addr & m->s_addr))
236 		return (1);
237 	return (0);
238 }
239 
240 /*
241  * Return 1 if the addresses a (with mask m) matches address b
242  * otherwise return 0. It is assumed that address a has been
243  * pre-masked out, we only need to mask b.
244  */
245 static int
246 match_addr_v6(struct sdaddr_v6 *a, struct sdaddr_v6 *m, struct sdaddr_v6 *b)
247 {
248 	if (((a->addr32[0]) == (b->addr32[0] & m->addr32[0])) &&
249 	    ((a->addr32[1]) == (b->addr32[1] & m->addr32[1])) &&
250 	    ((a->addr32[2]) == (b->addr32[2] & m->addr32[2])) &&
251 	    ((a->addr32[3]) == (b->addr32[3] & m->addr32[3])))
252 		return (1);
253 	return (0);
254 }
255 
256 #define grow_sdlist(sd, c, l) do {					       \
257 	if (c == l) {							       \
258 		struct sdlist **tmp;					       \
259 									       \
260 		tmp = reallocarray(sd, l + 128, sizeof(struct sdlist *));      \
261 		if (tmp == NULL) {					       \
262 			/*						       \
263 			 * XXX out of memory - return what we have	       \
264 			 */						       \
265 			return (sdnew);					       \
266 		}							       \
267 		sd = tmp;						       \
268 		l += 128;						       \
269 	}								       \
270 } while (0)
271 
272 static struct sdlist **
273 sdl_lookup_v4(struct sdlist *sdl, struct in_addr *src)
274 {
275 	struct sdentry_v4 *entry;
276 	int i, matches = 0;
277 	int sdnewlen = 0;
278 	struct sdlist **sdnew = NULL;
279 
280 	while (sdl->tag != NULL) {
281 		for (i = 0; i < sdl->v4.naddrs; i++) {
282 			entry = &sdl->v4.addrs[i];
283 			if (match_addr_v4(&entry->sda, &entry->sdm, src)) {
284 				grow_sdlist(sdnew, matches, sdnewlen);
285 				sdnew[matches] = sdl;
286 				matches++;
287 				sdnew[matches] = NULL;
288 				break;
289 			}
290 		}
291 		sdl++;
292 	}
293 	return (sdnew);
294 }
295 
296 static struct sdlist **
297 sdl_lookup_v6(struct sdlist *sdl, struct sdaddr_v6 *src)
298 {
299 	struct sdentry_v6 *entry;
300 	int i, matches = 0;
301 	int sdnewlen = 0;
302 	struct sdlist **sdnew = NULL;
303 
304 	while (sdl->tag != NULL) {
305 		for (i = 0; i < sdl->v6.naddrs; i++) {
306 			entry = &sdl->v6.addrs[i];
307 			if (match_addr_v6(&entry->sda, &entry->sdm, src)) {
308 				grow_sdlist(sdnew, matches, sdnewlen);
309 				sdnew[matches] = sdl;
310 				matches++;
311 				sdnew[matches] = NULL;
312 				break;
313 			}
314 		}
315 		sdl++;
316 	}
317 	return (sdnew);
318 }
319 
320 /*
321  * Given an address and address family
322  * return list of pointers to matching nodes. or NULL if none.
323  */
324 struct sdlist **
325 sdl_lookup(struct sdlist *head, int af, void *src)
326 {
327 	if (head == NULL)
328 		return (NULL);
329 
330 	switch (af) {
331 	case AF_INET:
332 		return (sdl_lookup_v4(head, src));
333 	case AF_INET6:
334 		return (sdl_lookup_v6(head, src));
335 	default:
336 		return (NULL);
337 	}
338 }
339 
340 static void
341 sdl_free(struct sdlist *sdl)
342 {
343 	free(sdl->tag);
344 	free(sdl->string);
345 	free(sdl->v4.addrs);
346 	free(sdl->v6.addrs);
347 	sdl_clear(sdl);
348 }
349 
350 static void
351 sdl_clear(struct sdlist *sdl)
352 {
353 	sdl->tag = NULL;
354 	sdl->string = NULL;
355 	sdl->v4.addrs = NULL;
356 	sdl->v4.naddrs = 0;
357 	sdl->v6.addrs = NULL;
358 	sdl->v6.naddrs = 0;
359 }
360