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