1 /* $OpenBSD: grey.c,v 1.66 2018/10/25 06:42:35 mestre Exp $ */ 2 3 /* 4 * Copyright (c) 2004-2006 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 #include <sys/types.h> 20 #include <sys/socket.h> 21 #include <sys/ioctl.h> 22 #include <sys/wait.h> 23 #include <net/if.h> 24 #include <netinet/in.h> 25 #include <net/pfvar.h> 26 #include <ctype.h> 27 #include <db.h> 28 #include <errno.h> 29 #include <fcntl.h> 30 #include <pwd.h> 31 #include <signal.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <syslog.h> 36 #include <time.h> 37 #include <unistd.h> 38 #include <netdb.h> 39 40 #include "grey.h" 41 #include "sync.h" 42 43 extern time_t passtime, greyexp, whiteexp, trapexp; 44 extern struct syslog_data sdata; 45 extern struct passwd *pw; 46 extern u_short cfg_port; 47 extern pid_t jail_pid; 48 extern FILE *trapcfg; 49 extern FILE *grey; 50 extern int debug; 51 extern int syncsend; 52 extern int greyback[2]; 53 54 /* From netinet/in.h, but only _KERNEL_ gets them. */ 55 #define satosin(sa) ((struct sockaddr_in *)(sa)) 56 #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) 57 58 void configure_spamd(char **, u_int, FILE *); 59 int configure_pf(char **, int); 60 char *dequotetolower(const char *); 61 void readsuffixlists(void); 62 void freeaddrlists(void); 63 int addwhiteaddr(char *); 64 int addtrapaddr(char *); 65 int db_addrstate(DB *, char *); 66 int greyscan(char *); 67 int trapcheck(DB *, char *); 68 int twupdate(char *, char *, char *, char *, char *); 69 int twread(char *); 70 int greyreader(void); 71 void greyscanner(void); 72 73 74 u_int whitecount, whitealloc; 75 u_int trapcount, trapalloc; 76 char **whitelist; 77 char **traplist; 78 79 char *traplist_name = "spamd-greytrap"; 80 char *traplist_msg = "\"Your address %A has mailed to spamtraps here\\n\""; 81 82 pid_t db_pid = -1; 83 int pfdev; 84 85 struct db_change { 86 SLIST_ENTRY(db_change) entry; 87 char * key; 88 void * data; 89 size_t dsiz; 90 int act; 91 }; 92 93 #define DBC_ADD 1 94 #define DBC_DEL 2 95 96 /* db pending changes list */ 97 SLIST_HEAD(, db_change) db_changes = SLIST_HEAD_INITIALIZER(db_changes); 98 99 struct mail_addr { 100 SLIST_ENTRY(mail_addr) entry; 101 char addr[MAX_MAIL]; 102 }; 103 104 /* list of suffixes that must match TO: */ 105 SLIST_HEAD(, mail_addr) match_suffix = SLIST_HEAD_INITIALIZER(match_suffix); 106 char *alloweddomains_file = PATH_SPAMD_ALLOWEDDOMAINS; 107 108 char *low_prio_mx_ip; 109 time_t startup; 110 111 static char *pargv[11]= { 112 "pfctl", "-p", "/dev/pf", "-q", "-t", 113 "spamd-white", "-T", "replace", "-f", "-", NULL 114 }; 115 116 /* If the parent gets a signal, kill off the children and exit */ 117 /* ARGSUSED */ 118 static void 119 sig_term_chld(int sig) 120 { 121 if (db_pid != -1) 122 kill(db_pid, SIGTERM); 123 if (jail_pid != -1) 124 kill(jail_pid, SIGTERM); 125 _exit(1); 126 } 127 128 /* 129 * Greatly simplified version from spamd_setup.c - only 130 * sends one blacklist to an already open stream. Has no need 131 * to collapse cidr ranges since these are only ever single 132 * host hits. 133 */ 134 void 135 configure_spamd(char **addrs, u_int count, FILE *sdc) 136 { 137 u_int i; 138 139 /* XXX - doesn't support IPV6 yet */ 140 fprintf(sdc, "%s;", traplist_name); 141 if (count != 0) { 142 fprintf(sdc, "%s;inet;%u", traplist_msg, count); 143 for (i = 0; i < count; i++) 144 fprintf(sdc, ";%s/32", addrs[i]); 145 } 146 fputc('\n', sdc); 147 if (fflush(sdc) == EOF) 148 syslog_r(LOG_DEBUG, &sdata, "configure_spamd: fflush failed (%m)"); 149 } 150 151 int 152 configure_pf(char **addrs, int count) 153 { 154 FILE *pf = NULL; 155 int i, pdes[2], status; 156 pid_t pid; 157 char *fdpath; 158 struct sigaction sa; 159 160 sigfillset(&sa.sa_mask); 161 sa.sa_flags = SA_RESTART; 162 sa.sa_handler = sig_term_chld; 163 164 if (debug) 165 fprintf(stderr, "configure_pf - device on fd %d\n", pfdev); 166 167 /* Because /dev/fd/ only contains device nodes for 0-63 */ 168 if (pfdev < 1 || pfdev > 63) 169 return(-1); 170 171 if (asprintf(&fdpath, "/dev/fd/%d", pfdev) == -1) 172 return(-1); 173 pargv[2] = fdpath; 174 if (pipe(pdes) != 0) { 175 syslog_r(LOG_INFO, &sdata, "pipe failed (%m)"); 176 free(fdpath); 177 fdpath = NULL; 178 return(-1); 179 } 180 signal(SIGCHLD, SIG_DFL); 181 switch (pid = fork()) { 182 case -1: 183 syslog_r(LOG_INFO, &sdata, "fork failed (%m)"); 184 free(fdpath); 185 fdpath = NULL; 186 close(pdes[0]); 187 close(pdes[1]); 188 sigaction(SIGCHLD, &sa, NULL); 189 return(-1); 190 case 0: 191 /* child */ 192 close(pdes[1]); 193 if (pdes[0] != STDIN_FILENO) { 194 dup2(pdes[0], STDIN_FILENO); 195 close(pdes[0]); 196 } 197 execvp(PATH_PFCTL, pargv); 198 syslog_r(LOG_ERR, &sdata, "can't exec %s:%m", PATH_PFCTL); 199 _exit(1); 200 } 201 202 /* parent */ 203 free(fdpath); 204 fdpath = NULL; 205 close(pdes[0]); 206 pf = fdopen(pdes[1], "w"); 207 if (pf == NULL) { 208 syslog_r(LOG_INFO, &sdata, "fdopen failed (%m)"); 209 close(pdes[1]); 210 sigaction(SIGCHLD, &sa, NULL); 211 return(-1); 212 } 213 for (i = 0; i < count; i++) 214 if (addrs[i] != NULL) 215 fprintf(pf, "%s/32\n", addrs[i]); 216 fclose(pf); 217 218 waitpid(pid, &status, 0); 219 if (WIFEXITED(status) && WEXITSTATUS(status) != 0) 220 syslog_r(LOG_ERR, &sdata, "%s returned status %d", PATH_PFCTL, 221 WEXITSTATUS(status)); 222 else if (WIFSIGNALED(status)) 223 syslog_r(LOG_ERR, &sdata, "%s died on signal %d", PATH_PFCTL, 224 WTERMSIG(status)); 225 226 sigaction(SIGCHLD, &sa, NULL); 227 return(0); 228 } 229 230 char * 231 dequotetolower(const char *addr) 232 { 233 static char buf[MAX_MAIL]; 234 char *cp; 235 236 if (*addr == '<') 237 addr++; 238 (void) strlcpy(buf, addr, sizeof(buf)); 239 cp = strrchr(buf, '>'); 240 if (cp != NULL && cp[1] == '\0') 241 *cp = '\0'; 242 cp = buf; 243 while (*cp != '\0') { 244 *cp = tolower((unsigned char)*cp); 245 cp++; 246 } 247 return(buf); 248 } 249 250 void 251 readsuffixlists(void) 252 { 253 FILE *fp; 254 char *buf; 255 size_t len; 256 struct mail_addr *m; 257 258 while (!SLIST_EMPTY(&match_suffix)) { 259 m = SLIST_FIRST(&match_suffix); 260 SLIST_REMOVE_HEAD(&match_suffix, entry); 261 free(m); 262 } 263 if ((fp = fopen(alloweddomains_file, "r")) != NULL) { 264 while ((buf = fgetln(fp, &len))) { 265 /* strip white space-characters */ 266 while (len > 0 && isspace((unsigned char)buf[len-1])) 267 len--; 268 while (len > 0 && isspace((unsigned char)*buf)) { 269 buf++; 270 len--; 271 } 272 if (len == 0) 273 continue; 274 /* jump over comments and blank lines */ 275 if (*buf == '#' || *buf == '\n') 276 continue; 277 if (buf[len-1] == '\n') 278 len--; 279 if ((len + 1) > sizeof(m->addr)) { 280 syslog_r(LOG_ERR, &sdata, 281 "line too long in %s - file ignored", 282 alloweddomains_file); 283 goto bad; 284 } 285 if ((m = malloc(sizeof(struct mail_addr))) == NULL) 286 goto bad; 287 memcpy(m->addr, buf, len); 288 m->addr[len]='\0'; 289 syslog_r(LOG_ERR, &sdata, "got suffix %s", m->addr); 290 SLIST_INSERT_HEAD(&match_suffix, m, entry); 291 } 292 } 293 return; 294 bad: 295 while (!SLIST_EMPTY(&match_suffix)) { 296 m = SLIST_FIRST(&match_suffix); 297 SLIST_REMOVE_HEAD(&match_suffix, entry); 298 free(m); 299 } 300 } 301 302 void 303 freeaddrlists(void) 304 { 305 int i; 306 307 if (whitelist != NULL) 308 for (i = 0; i < whitecount; i++) { 309 free(whitelist[i]); 310 whitelist[i] = NULL; 311 } 312 whitecount = 0; 313 if (traplist != NULL) { 314 for (i = 0; i < trapcount; i++) { 315 free(traplist[i]); 316 traplist[i] = NULL; 317 } 318 } 319 trapcount = 0; 320 } 321 322 /* validate, then add to list of addrs to whitelist */ 323 int 324 addwhiteaddr(char *addr) 325 { 326 struct addrinfo hints, *res; 327 char ch; 328 329 memset(&hints, 0, sizeof(hints)); 330 hints.ai_family = AF_INET; /*for now*/ 331 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 332 hints.ai_protocol = IPPROTO_UDP; /*dummy*/ 333 hints.ai_flags = AI_NUMERICHOST; 334 335 if (getaddrinfo(addr, NULL, &hints, &res) != 0) 336 return(-1); 337 338 /* Check spamd blacklists in main process. */ 339 if (send(greyback[0], res->ai_addr, res->ai_addr->sa_len, 0) == -1) { 340 syslog_r(LOG_ERR, &sdata, "%s: send: %m", __func__); 341 } else { 342 if (recv(greyback[0], &ch, sizeof(ch), 0) == 1) { 343 if (ch == '1') { 344 syslog_r(LOG_DEBUG, &sdata, 345 "%s blacklisted, removing from whitelist", 346 addr); 347 freeaddrinfo(res); 348 return(-1); 349 } 350 } 351 } 352 353 if (whitecount == whitealloc) { 354 char **tmp; 355 356 tmp = reallocarray(whitelist, 357 whitealloc + 1024, sizeof(char *)); 358 if (tmp == NULL) { 359 freeaddrinfo(res); 360 return(-1); 361 } 362 whitelist = tmp; 363 whitealloc += 1024; 364 } 365 whitelist[whitecount] = strdup(addr); 366 if (whitelist[whitecount] == NULL) { 367 freeaddrinfo(res); 368 return(-1); 369 } 370 whitecount++; 371 freeaddrinfo(res); 372 return(0); 373 } 374 375 /* validate, then add to list of addrs to traplist */ 376 int 377 addtrapaddr(char *addr) 378 { 379 struct addrinfo hints, *res; 380 381 memset(&hints, 0, sizeof(hints)); 382 hints.ai_family = AF_INET; /*for now*/ 383 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 384 hints.ai_protocol = IPPROTO_UDP; /*dummy*/ 385 hints.ai_flags = AI_NUMERICHOST; 386 387 if (getaddrinfo(addr, NULL, &hints, &res) == 0) { 388 if (trapcount == trapalloc) { 389 char **tmp; 390 391 tmp = reallocarray(traplist, 392 trapalloc + 1024, sizeof(char *)); 393 if (tmp == NULL) { 394 freeaddrinfo(res); 395 return(-1); 396 } 397 traplist = tmp; 398 trapalloc += 1024; 399 } 400 traplist[trapcount] = strdup(addr); 401 if (traplist[trapcount] == NULL) { 402 freeaddrinfo(res); 403 return(-1); 404 } 405 trapcount++; 406 freeaddrinfo(res); 407 } else 408 return(-1); 409 return(0); 410 } 411 412 static int 413 queue_change(char *key, char *data, size_t dsiz, int act) 414 { 415 struct db_change *dbc; 416 417 if ((dbc = malloc(sizeof(*dbc))) == NULL) { 418 syslog_r(LOG_DEBUG, &sdata, "malloc failed (queue change)"); 419 return(-1); 420 } 421 if ((dbc->key = strdup(key)) == NULL) { 422 syslog_r(LOG_DEBUG, &sdata, "malloc failed (queue change)"); 423 free(dbc); 424 return(-1); 425 } 426 if ((dbc->data = malloc(dsiz)) == NULL) { 427 syslog_r(LOG_DEBUG, &sdata, "malloc failed (queue change)"); 428 free(dbc->key); 429 free(dbc); 430 return(-1); 431 } 432 memcpy(dbc->data, data, dsiz); 433 dbc->dsiz = dsiz; 434 dbc->act = act; 435 syslog_r(LOG_DEBUG, &sdata, 436 "queueing %s of %s", ((act == DBC_ADD) ? "add" : "deletion"), 437 dbc->key); 438 SLIST_INSERT_HEAD(&db_changes, dbc, entry); 439 return(0); 440 } 441 442 static int 443 do_changes(DB *db) 444 { 445 DBT dbk, dbd; 446 struct db_change *dbc; 447 int ret = 0; 448 449 while (!SLIST_EMPTY(&db_changes)) { 450 dbc = SLIST_FIRST(&db_changes); 451 switch (dbc->act) { 452 case DBC_ADD: 453 memset(&dbk, 0, sizeof(dbk)); 454 dbk.size = strlen(dbc->key); 455 dbk.data = dbc->key; 456 memset(&dbd, 0, sizeof(dbd)); 457 dbd.size = dbc->dsiz; 458 dbd.data = dbc->data; 459 if (db->put(db, &dbk, &dbd, 0)) { 460 db->sync(db, 0); 461 syslog_r(LOG_ERR, &sdata, 462 "can't add %s to spamd db (%m)", dbc->key); 463 ret = -1; 464 } 465 db->sync(db, 0); 466 break; 467 case DBC_DEL: 468 memset(&dbk, 0, sizeof(dbk)); 469 dbk.size = strlen(dbc->key); 470 dbk.data = dbc->key; 471 if (db->del(db, &dbk, 0)) { 472 syslog_r(LOG_ERR, &sdata, 473 "can't delete %s from spamd db (%m)", 474 dbc->key); 475 ret = -1; 476 } 477 break; 478 default: 479 syslog_r(LOG_ERR, &sdata, "Unrecognized db change"); 480 ret = -1; 481 } 482 free(dbc->key); 483 dbc->key = NULL; 484 free(dbc->data); 485 dbc->data = NULL; 486 dbc->act = 0; 487 dbc->dsiz = 0; 488 SLIST_REMOVE_HEAD(&db_changes, entry); 489 free(dbc); 490 491 } 492 return(ret); 493 } 494 495 /* -1=error, 0=notfound, 1=TRAPPED, 2=WHITE */ 496 int 497 db_addrstate(DB *db, char *key) 498 { 499 DBT dbk, dbd; 500 struct gdata gd; 501 502 memset(&dbk, 0, sizeof(dbk)); 503 dbk.size = strlen(key); 504 dbk.data = key; 505 memset(&dbd, 0, sizeof(dbd)); 506 switch (db->get(db, &dbk, &dbd, 0)) { 507 case 1: 508 /* not found */ 509 return (0); 510 case 0: 511 if (gdcopyin(&dbd, &gd) != -1) 512 return (gd.pcount == -1 ? 1 : 2); 513 /* FALLTHROUGH */ 514 default: 515 /* error */ 516 return (-1); 517 } 518 } 519 520 521 int 522 greyscan(char *dbname) 523 { 524 HASHINFO hashinfo; 525 DBT dbk, dbd; 526 DB *db; 527 struct gdata gd; 528 int r; 529 char *a = NULL; 530 size_t asiz = 0; 531 time_t now = time(NULL); 532 533 /* walk db, expire, and whitelist */ 534 memset(&hashinfo, 0, sizeof(hashinfo)); 535 db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 536 if (db == NULL) { 537 syslog_r(LOG_INFO, &sdata, "dbopen failed (%m)"); 538 return(-1); 539 } 540 memset(&dbk, 0, sizeof(dbk)); 541 memset(&dbd, 0, sizeof(dbd)); 542 for (r = db->seq(db, &dbk, &dbd, R_FIRST); !r; 543 r = db->seq(db, &dbk, &dbd, R_NEXT)) { 544 if ((dbk.size < 1) || gdcopyin(&dbd, &gd) == -1) { 545 syslog_r(LOG_ERR, &sdata, "bogus entry in spamd database"); 546 goto bad; 547 } 548 if (asiz < dbk.size + 1) { 549 char *tmp; 550 551 tmp = reallocarray(a, dbk.size, 2); 552 if (tmp == NULL) 553 goto bad; 554 a = tmp; 555 asiz = dbk.size * 2; 556 } 557 memset(a, 0, asiz); 558 memcpy(a, dbk.data, dbk.size); 559 if (gd.expire <= now && gd.pcount != -2) { 560 /* get rid of entry */ 561 if (queue_change(a, NULL, 0, DBC_DEL) == -1) 562 goto bad; 563 } else if (gd.pcount == -1) { 564 /* this is a greytrap hit */ 565 if ((addtrapaddr(a) == -1) && 566 (queue_change(a, NULL, 0, DBC_DEL) == -1)) 567 goto bad; 568 } else if (gd.pcount >= 0 && gd.pass <= now) { 569 int tuple = 0; 570 char *cp; 571 int state; 572 573 /* 574 * if not already TRAPPED, 575 * add address to whitelist 576 * add an address-keyed entry to db 577 */ 578 cp = strchr(a, '\n'); 579 if (cp != NULL) { 580 tuple = 1; 581 *cp = '\0'; 582 } 583 584 state = db_addrstate(db, a); 585 if (state != 1 && addwhiteaddr(a) == -1) { 586 if (cp != NULL) 587 *cp = '\n'; 588 if (queue_change(a, NULL, 0, DBC_DEL) == -1) 589 goto bad; 590 } 591 592 if (tuple && state <= 0) { 593 if (cp != NULL) 594 *cp = '\0'; 595 /* re-add entry, keyed only by ip */ 596 gd.expire = now + whiteexp; 597 dbd.size = sizeof(gd); 598 dbd.data = &gd; 599 if (queue_change(a, (void *) &gd, sizeof(gd), 600 DBC_ADD) == -1) 601 goto bad; 602 syslog_r(LOG_DEBUG, &sdata, 603 "whitelisting %s in %s", a, dbname); 604 } 605 if (debug) 606 fprintf(stderr, "whitelisted %s\n", a); 607 } 608 } 609 (void) do_changes(db); 610 db->close(db); 611 db = NULL; 612 configure_pf(whitelist, whitecount); 613 configure_spamd(traplist, trapcount, trapcfg); 614 615 freeaddrlists(); 616 free(a); 617 a = NULL; 618 return(0); 619 bad: 620 (void) do_changes(db); 621 db->close(db); 622 db = NULL; 623 freeaddrlists(); 624 free(a); 625 a = NULL; 626 return(-1); 627 } 628 629 int 630 trapcheck(DB *db, char *to) 631 { 632 int i, j, smatch = 0; 633 DBT dbk, dbd; 634 struct mail_addr *m; 635 char * trap; 636 size_t s; 637 638 trap = dequotetolower(to); 639 if (!SLIST_EMPTY(&match_suffix)) { 640 s = strlen(trap); 641 SLIST_FOREACH(m, &match_suffix, entry) { 642 j = s - strlen(m->addr); 643 if ((j >= 0) && (strcasecmp(trap+j, m->addr) == 0)) 644 smatch = 1; 645 } 646 if (!smatch) 647 /* no suffixes match, so trap it */ 648 return (0); 649 } 650 memset(&dbk, 0, sizeof(dbk)); 651 dbk.size = strlen(trap); 652 dbk.data = trap; 653 memset(&dbd, 0, sizeof(dbd)); 654 i = db->get(db, &dbk, &dbd, 0); 655 if (i == -1) 656 return (-1); 657 if (i) 658 /* didn't exist - so this doesn't match a known spamtrap */ 659 return (1); 660 else 661 /* To: address is a spamtrap, so add as a greytrap entry */ 662 return (0); 663 } 664 665 int 666 twupdate(char *dbname, char *what, char *ip, char *source, char *expires) 667 { 668 /* we got a TRAP or WHITE update from someone else */ 669 HASHINFO hashinfo; 670 DBT dbk, dbd; 671 DB *db; 672 struct gdata gd; 673 time_t now, expire; 674 int r, spamtrap; 675 676 now = time(NULL); 677 /* expiry times have to be in the future */ 678 expire = strtonum(expires, now, 679 sizeof(time_t) == sizeof(int) ? INT_MAX : LLONG_MAX, NULL); 680 if (expire == 0) 681 return(-1); 682 683 if (strcmp(what, "TRAP") == 0) 684 spamtrap = 1; 685 else if (strcmp(what, "WHITE") == 0) 686 spamtrap = 0; 687 else 688 return(-1); 689 690 memset(&hashinfo, 0, sizeof(hashinfo)); 691 db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 692 if (db == NULL) 693 return(-1); 694 695 memset(&dbk, 0, sizeof(dbk)); 696 dbk.size = strlen(ip); 697 dbk.data = ip; 698 memset(&dbd, 0, sizeof(dbd)); 699 r = db->get(db, &dbk, &dbd, 0); 700 if (r == -1) 701 goto bad; 702 if (r) { 703 /* new entry */ 704 memset(&gd, 0, sizeof(gd)); 705 gd.first = now; 706 gd.pcount = spamtrap ? -1 : 0; 707 gd.expire = expire; 708 memset(&dbk, 0, sizeof(dbk)); 709 dbk.size = strlen(ip); 710 dbk.data = ip; 711 memset(&dbd, 0, sizeof(dbd)); 712 dbd.size = sizeof(gd); 713 dbd.data = &gd; 714 r = db->put(db, &dbk, &dbd, 0); 715 db->sync(db, 0); 716 if (r) 717 goto bad; 718 if (debug) 719 fprintf(stderr, "added %s %s\n", 720 spamtrap ? "trap entry for" : "", ip); 721 syslog_r(LOG_DEBUG, &sdata, 722 "new %s from %s for %s, expires %s", what, source, ip, 723 expires); 724 } else { 725 /* existing entry */ 726 if (gdcopyin(&dbd, &gd) == -1) { 727 /* whatever this is, it doesn't belong */ 728 db->del(db, &dbk, 0); 729 db->sync(db, 0); 730 goto bad; 731 } 732 if (spamtrap) { 733 gd.pcount = -1; 734 gd.bcount++; 735 } else 736 gd.pcount++; 737 memset(&dbk, 0, sizeof(dbk)); 738 dbk.size = strlen(ip); 739 dbk.data = ip; 740 memset(&dbd, 0, sizeof(dbd)); 741 dbd.size = sizeof(gd); 742 dbd.data = &gd; 743 r = db->put(db, &dbk, &dbd, 0); 744 db->sync(db, 0); 745 if (r) 746 goto bad; 747 if (debug) 748 fprintf(stderr, "updated %s\n", ip); 749 } 750 db->close(db); 751 return(0); 752 bad: 753 db->close(db); 754 return(-1); 755 756 } 757 758 int 759 greyupdate(char *dbname, char *helo, char *ip, char *from, char *to, int sync, 760 char *cip) 761 { 762 HASHINFO hashinfo; 763 DBT dbk, dbd; 764 DB *db; 765 char *key = NULL; 766 char *lookup; 767 struct gdata gd; 768 time_t now, expire; 769 int r, spamtrap; 770 771 now = time(NULL); 772 773 /* open with lock, find record, update, close, unlock */ 774 memset(&hashinfo, 0, sizeof(hashinfo)); 775 db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 776 if (db == NULL) 777 return(-1); 778 if (asprintf(&key, "%s\n%s\n%s\n%s", ip, helo, from, to) == -1) 779 goto bad; 780 r = trapcheck(db, to); 781 switch (r) { 782 case 1: 783 /* do not trap */ 784 spamtrap = 0; 785 lookup = key; 786 expire = greyexp; 787 break; 788 case 0: 789 /* trap */ 790 spamtrap = 1; 791 lookup = ip; 792 expire = trapexp; 793 syslog_r(LOG_DEBUG, &sdata, "Trapping %s for tuple %s", ip, 794 key); 795 break; 796 default: 797 goto bad; 798 break; 799 } 800 memset(&dbk, 0, sizeof(dbk)); 801 dbk.size = strlen(lookup); 802 dbk.data = lookup; 803 memset(&dbd, 0, sizeof(dbd)); 804 r = db->get(db, &dbk, &dbd, 0); 805 if (r == -1) 806 goto bad; 807 if (r) { 808 /* new entry */ 809 if (sync && low_prio_mx_ip && 810 (strcmp(cip, low_prio_mx_ip) == 0) && 811 ((startup + 60) < now)) { 812 /* we haven't seen a greylist entry for this tuple, 813 * and yet the connection was to a low priority MX 814 * which we know can't be hit first if the client 815 * is adhering to the RFC's - soo.. kill it! 816 */ 817 spamtrap = 1; 818 lookup = ip; 819 expire = trapexp; 820 syslog_r(LOG_DEBUG, &sdata, 821 "Trapping %s for trying %s first for tuple %s", 822 ip, low_prio_mx_ip, key); 823 } 824 memset(&gd, 0, sizeof(gd)); 825 gd.first = now; 826 gd.bcount = 1; 827 gd.pcount = spamtrap ? -1 : 0; 828 gd.pass = now + expire; 829 gd.expire = now + expire; 830 memset(&dbk, 0, sizeof(dbk)); 831 dbk.size = strlen(lookup); 832 dbk.data = lookup; 833 memset(&dbd, 0, sizeof(dbd)); 834 dbd.size = sizeof(gd); 835 dbd.data = &gd; 836 r = db->put(db, &dbk, &dbd, 0); 837 db->sync(db, 0); 838 if (r) 839 goto bad; 840 if (debug) 841 fprintf(stderr, "added %s %s\n", 842 spamtrap ? "greytrap entry for" : "", lookup); 843 syslog_r(LOG_DEBUG, &sdata, 844 "new %sentry %s from %s to %s, helo %s", 845 spamtrap ? "greytrap " : "", ip, from, to, helo); 846 } else { 847 /* existing entry */ 848 if (gdcopyin(&dbd, &gd) == -1) { 849 /* whatever this is, it doesn't belong */ 850 db->del(db, &dbk, 0); 851 db->sync(db, 0); 852 goto bad; 853 } 854 gd.bcount++; 855 gd.pcount = spamtrap ? -1 : 0; 856 if (gd.first + passtime < now) 857 gd.pass = now; 858 memset(&dbk, 0, sizeof(dbk)); 859 dbk.size = strlen(lookup); 860 dbk.data = lookup; 861 memset(&dbd, 0, sizeof(dbd)); 862 dbd.size = sizeof(gd); 863 dbd.data = &gd; 864 r = db->put(db, &dbk, &dbd, 0); 865 db->sync(db, 0); 866 if (r) 867 goto bad; 868 if (debug) 869 fprintf(stderr, "updated %s\n", lookup); 870 } 871 free(key); 872 key = NULL; 873 db->close(db); 874 db = NULL; 875 876 /* Entry successfully update, sent out sync message */ 877 if (syncsend && sync) { 878 if (spamtrap) { 879 syslog_r(LOG_DEBUG, &sdata, 880 "sync_trap %s", ip); 881 sync_trapped(now, now + expire, ip); 882 } 883 else 884 sync_update(now, helo, ip, from, to); 885 } 886 return(0); 887 bad: 888 free(key); 889 key = NULL; 890 db->close(db); 891 db = NULL; 892 return(-1); 893 } 894 895 int 896 twread(char *buf) 897 { 898 if ((strncmp(buf, "WHITE:", 6) == 0) || 899 (strncmp(buf, "TRAP:", 5) == 0)) { 900 char **ap, *argv[5]; 901 int argc = 0; 902 903 for (ap = argv; 904 ap < &argv[4] && (*ap = strsep(&buf, ":")) != NULL;) { 905 if (**ap != '\0') 906 ap++; 907 argc++; 908 } 909 *ap = NULL; 910 if (argc != 4) 911 return (-1); 912 twupdate(PATH_SPAMD_DB, argv[0], argv[1], argv[2], argv[3]); 913 return (0); 914 } else 915 return (-1); 916 } 917 918 int 919 greyreader(void) 920 { 921 char cip[32], ip[32], helo[MAX_MAIL], from[MAX_MAIL], to[MAX_MAIL]; 922 char *buf; 923 size_t len; 924 int state, sync; 925 struct addrinfo hints, *res; 926 927 memset(&hints, 0, sizeof(hints)); 928 hints.ai_family = AF_INET; /*for now*/ 929 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 930 hints.ai_protocol = IPPROTO_UDP; /*dummy*/ 931 hints.ai_flags = AI_NUMERICHOST; 932 933 state = 0; 934 sync = 1; 935 if (grey == NULL) { 936 syslog_r(LOG_ERR, &sdata, "No greylist pipe stream!\n"); 937 return (-1); 938 } 939 940 /* grab trap suffixes */ 941 readsuffixlists(); 942 943 while ((buf = fgetln(grey, &len))) { 944 if (buf[len - 1] == '\n') 945 buf[len - 1] = '\0'; 946 else 947 /* all valid lines end in \n */ 948 continue; 949 if (strlen(buf) < 4) 950 continue; 951 952 if (strcmp(buf, "SYNC") == 0) { 953 sync = 0; 954 continue; 955 } 956 957 switch (state) { 958 case 0: 959 if (twread(buf) == 0) { 960 state = 0; 961 break; 962 } 963 if (strncmp(buf, "HE:", 3) != 0) { 964 if (strncmp(buf, "CO:", 3) == 0) 965 strlcpy(cip, buf+3, sizeof(cip)); 966 state = 0; 967 break; 968 } 969 strlcpy(helo, buf+3, sizeof(helo)); 970 state = 1; 971 break; 972 case 1: 973 if (strncmp(buf, "IP:", 3) != 0) 974 break; 975 strlcpy(ip, buf+3, sizeof(ip)); 976 if (getaddrinfo(ip, NULL, &hints, &res) == 0) { 977 freeaddrinfo(res); 978 state = 2; 979 } else 980 state = 0; 981 break; 982 case 2: 983 if (strncmp(buf, "FR:", 3) != 0) { 984 state = 0; 985 break; 986 } 987 strlcpy(from, buf+3, sizeof(from)); 988 state = 3; 989 break; 990 case 3: 991 if (strncmp(buf, "TO:", 3) != 0) { 992 state = 0; 993 break; 994 } 995 strlcpy(to, buf+3, sizeof(to)); 996 if (debug) 997 fprintf(stderr, 998 "Got Grey HELO %s, IP %s from %s to %s\n", 999 helo, ip, from, to); 1000 greyupdate(PATH_SPAMD_DB, helo, ip, from, to, sync, cip); 1001 sync = 1; 1002 state = 0; 1003 break; 1004 } 1005 } 1006 return (0); 1007 } 1008 1009 void 1010 greyscanner(void) 1011 { 1012 for (;;) { 1013 if (greyscan(PATH_SPAMD_DB) == -1) 1014 syslog_r(LOG_NOTICE, &sdata, "scan of %s failed", 1015 PATH_SPAMD_DB); 1016 sleep(DB_SCAN_INTERVAL); 1017 } 1018 } 1019 1020 static void 1021 drop_privs(void) 1022 { 1023 /* 1024 * lose root, continue as non-root user 1025 */ 1026 if (setgroups(1, &pw->pw_gid) || 1027 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 1028 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) { 1029 syslog_r(LOG_ERR, &sdata, "failed to drop privs (%m)"); 1030 exit(1); 1031 } 1032 } 1033 1034 void 1035 check_spamd_db(void) 1036 { 1037 HASHINFO hashinfo; 1038 int i = -1; 1039 DB *db; 1040 1041 /* check to see if /var/db/spamd exists, if not, create it */ 1042 memset(&hashinfo, 0, sizeof(hashinfo)); 1043 db = dbopen(PATH_SPAMD_DB, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 1044 1045 if (db == NULL) { 1046 switch (errno) { 1047 case ENOENT: 1048 i = open(PATH_SPAMD_DB, O_RDWR|O_CREAT, 0644); 1049 if (i == -1) { 1050 syslog_r(LOG_ERR, &sdata, 1051 "create %s failed (%m)", PATH_SPAMD_DB); 1052 exit(1); 1053 } 1054 /* if we are dropping privs, chown to that user */ 1055 if (pw && (fchown(i, pw->pw_uid, pw->pw_gid) == -1)) { 1056 syslog_r(LOG_ERR, &sdata, 1057 "chown %s failed (%m)", PATH_SPAMD_DB); 1058 exit(1); 1059 } 1060 close(i); 1061 return; 1062 break; 1063 default: 1064 syslog_r(LOG_ERR, &sdata, "open of %s failed (%m)", 1065 PATH_SPAMD_DB); 1066 exit(1); 1067 } 1068 } 1069 db->sync(db, 0); 1070 db->close(db); 1071 } 1072 1073 1074 int 1075 greywatcher(void) 1076 { 1077 struct sigaction sa; 1078 1079 drop_privs(); 1080 1081 if (unveil(PATH_SPAMD_DB, "rw") == -1) { 1082 syslog_r(LOG_ERR, &sdata, "unveil failed (%m)"); 1083 exit(1); 1084 } 1085 if (unveil(alloweddomains_file, "r") == -1) { 1086 syslog_r(LOG_ERR, &sdata, "unveil failed (%m)"); 1087 exit(1); 1088 } 1089 if (unveil(PATH_PFCTL, "x") == -1) { 1090 syslog_r(LOG_ERR, &sdata, "unveil failed (%m)"); 1091 exit(1); 1092 } 1093 if (pledge("stdio rpath wpath inet flock proc exec", NULL) == -1) { 1094 syslog_r(LOG_ERR, &sdata, "pledge failed (%m)"); 1095 exit(1); 1096 } 1097 1098 startup = time(NULL); 1099 db_pid = fork(); 1100 switch (db_pid) { 1101 case -1: 1102 syslog_r(LOG_ERR, &sdata, "fork failed (%m)"); 1103 exit(1); 1104 case 0: 1105 /* 1106 * child, talks to jailed spamd over greypipe, 1107 * updates db. has no access to pf. 1108 */ 1109 close(pfdev); 1110 setproctitle("(%s update)", PATH_SPAMD_DB); 1111 if (greyreader() == -1) { 1112 syslog_r(LOG_ERR, &sdata, "greyreader failed (%m)"); 1113 _exit(1); 1114 } 1115 _exit(0); 1116 } 1117 1118 1119 fclose(grey); 1120 /* 1121 * parent, scans db periodically for changes and updates 1122 * pf whitelist table accordingly. 1123 */ 1124 1125 sigfillset(&sa.sa_mask); 1126 sa.sa_flags = SA_RESTART; 1127 sa.sa_handler = sig_term_chld; 1128 sigaction(SIGTERM, &sa, NULL); 1129 sigaction(SIGHUP, &sa, NULL); 1130 sigaction(SIGCHLD, &sa, NULL); 1131 sigaction(SIGINT, &sa, NULL); 1132 1133 setproctitle("(pf <spamd-white> update)"); 1134 greyscanner(); 1135 exit(1); 1136 } 1137