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