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 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 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 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 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 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 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 ** 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 ** 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 ** 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 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 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 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 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 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