1 /* $OpenBSD: grey.c,v 1.63 2016/03/25 16:31:32 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 asiz = 0; 602 return(0); 603 bad: 604 (void) do_changes(db); 605 db->close(db); 606 db = NULL; 607 freeaddrlists(); 608 free(a); 609 a = NULL; 610 asiz = 0; 611 return(-1); 612 } 613 614 int 615 trapcheck(DB *db, char *to) 616 { 617 int i, j, smatch = 0; 618 DBT dbk, dbd; 619 struct mail_addr *m; 620 char * trap; 621 size_t s; 622 623 trap = dequotetolower(to); 624 if (!SLIST_EMPTY(&match_suffix)) { 625 s = strlen(trap); 626 SLIST_FOREACH(m, &match_suffix, entry) { 627 j = s - strlen(m->addr); 628 if ((j >= 0) && (strcasecmp(trap+j, m->addr) == 0)) 629 smatch = 1; 630 } 631 if (!smatch) 632 /* no suffixes match, so trap it */ 633 return (0); 634 } 635 memset(&dbk, 0, sizeof(dbk)); 636 dbk.size = strlen(trap); 637 dbk.data = trap; 638 memset(&dbd, 0, sizeof(dbd)); 639 i = db->get(db, &dbk, &dbd, 0); 640 if (i == -1) 641 return (-1); 642 if (i) 643 /* didn't exist - so this doesn't match a known spamtrap */ 644 return (1); 645 else 646 /* To: address is a spamtrap, so add as a greytrap entry */ 647 return (0); 648 } 649 650 int 651 twupdate(char *dbname, char *what, char *ip, char *source, char *expires) 652 { 653 /* we got a TRAP or WHITE update from someone else */ 654 HASHINFO hashinfo; 655 DBT dbk, dbd; 656 DB *db; 657 struct gdata gd; 658 time_t now, expire; 659 int r, spamtrap; 660 661 now = time(NULL); 662 /* expiry times have to be in the future */ 663 expire = strtonum(expires, now, 664 sizeof(time_t) == sizeof(int) ? INT_MAX : LLONG_MAX, NULL); 665 if (expire == 0) 666 return(-1); 667 668 if (strcmp(what, "TRAP") == 0) 669 spamtrap = 1; 670 else if (strcmp(what, "WHITE") == 0) 671 spamtrap = 0; 672 else 673 return(-1); 674 675 memset(&hashinfo, 0, sizeof(hashinfo)); 676 db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 677 if (db == NULL) 678 return(-1); 679 680 memset(&dbk, 0, sizeof(dbk)); 681 dbk.size = strlen(ip); 682 dbk.data = ip; 683 memset(&dbd, 0, sizeof(dbd)); 684 r = db->get(db, &dbk, &dbd, 0); 685 if (r == -1) 686 goto bad; 687 if (r) { 688 /* new entry */ 689 memset(&gd, 0, sizeof(gd)); 690 gd.first = now; 691 gd.pcount = spamtrap ? -1 : 0; 692 gd.expire = expire; 693 memset(&dbk, 0, sizeof(dbk)); 694 dbk.size = strlen(ip); 695 dbk.data = ip; 696 memset(&dbd, 0, sizeof(dbd)); 697 dbd.size = sizeof(gd); 698 dbd.data = &gd; 699 r = db->put(db, &dbk, &dbd, 0); 700 db->sync(db, 0); 701 if (r) 702 goto bad; 703 if (debug) 704 fprintf(stderr, "added %s %s\n", 705 spamtrap ? "trap entry for" : "", ip); 706 syslog_r(LOG_DEBUG, &sdata, 707 "new %s from %s for %s, expires %s", what, source, ip, 708 expires); 709 } else { 710 /* existing entry */ 711 if (gdcopyin(&dbd, &gd) == -1) { 712 /* whatever this is, it doesn't belong */ 713 db->del(db, &dbk, 0); 714 db->sync(db, 0); 715 goto bad; 716 } 717 if (spamtrap) { 718 gd.pcount = -1; 719 gd.bcount++; 720 } else 721 gd.pcount++; 722 memset(&dbk, 0, sizeof(dbk)); 723 dbk.size = strlen(ip); 724 dbk.data = ip; 725 memset(&dbd, 0, sizeof(dbd)); 726 dbd.size = sizeof(gd); 727 dbd.data = &gd; 728 r = db->put(db, &dbk, &dbd, 0); 729 db->sync(db, 0); 730 if (r) 731 goto bad; 732 if (debug) 733 fprintf(stderr, "updated %s\n", ip); 734 } 735 db->close(db); 736 return(0); 737 bad: 738 db->close(db); 739 return(-1); 740 741 } 742 743 int 744 greyupdate(char *dbname, char *helo, char *ip, char *from, char *to, int sync, 745 char *cip) 746 { 747 HASHINFO hashinfo; 748 DBT dbk, dbd; 749 DB *db; 750 char *key = NULL; 751 char *lookup; 752 struct gdata gd; 753 time_t now, expire; 754 int r, spamtrap; 755 756 now = time(NULL); 757 758 /* open with lock, find record, update, close, unlock */ 759 memset(&hashinfo, 0, sizeof(hashinfo)); 760 db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 761 if (db == NULL) 762 return(-1); 763 if (asprintf(&key, "%s\n%s\n%s\n%s", ip, helo, from, to) == -1) 764 goto bad; 765 r = trapcheck(db, to); 766 switch (r) { 767 case 1: 768 /* do not trap */ 769 spamtrap = 0; 770 lookup = key; 771 expire = greyexp; 772 break; 773 case 0: 774 /* trap */ 775 spamtrap = 1; 776 lookup = ip; 777 expire = trapexp; 778 syslog_r(LOG_DEBUG, &sdata, "Trapping %s for tuple %s", ip, 779 key); 780 break; 781 default: 782 goto bad; 783 break; 784 } 785 memset(&dbk, 0, sizeof(dbk)); 786 dbk.size = strlen(lookup); 787 dbk.data = lookup; 788 memset(&dbd, 0, sizeof(dbd)); 789 r = db->get(db, &dbk, &dbd, 0); 790 if (r == -1) 791 goto bad; 792 if (r) { 793 /* new entry */ 794 if (sync && low_prio_mx_ip && 795 (strcmp(cip, low_prio_mx_ip) == 0) && 796 ((startup + 60) < now)) { 797 /* we haven't seen a greylist entry for this tuple, 798 * and yet the connection was to a low priority MX 799 * which we know can't be hit first if the client 800 * is adhering to the RFC's - soo.. kill it! 801 */ 802 spamtrap = 1; 803 lookup = ip; 804 expire = trapexp; 805 syslog_r(LOG_DEBUG, &sdata, 806 "Trapping %s for trying %s first for tuple %s", 807 ip, low_prio_mx_ip, key); 808 } 809 memset(&gd, 0, sizeof(gd)); 810 gd.first = now; 811 gd.bcount = 1; 812 gd.pcount = spamtrap ? -1 : 0; 813 gd.pass = now + expire; 814 gd.expire = now + expire; 815 memset(&dbk, 0, sizeof(dbk)); 816 dbk.size = strlen(lookup); 817 dbk.data = lookup; 818 memset(&dbd, 0, sizeof(dbd)); 819 dbd.size = sizeof(gd); 820 dbd.data = &gd; 821 r = db->put(db, &dbk, &dbd, 0); 822 db->sync(db, 0); 823 if (r) 824 goto bad; 825 if (debug) 826 fprintf(stderr, "added %s %s\n", 827 spamtrap ? "greytrap entry for" : "", lookup); 828 syslog_r(LOG_DEBUG, &sdata, 829 "new %sentry %s from %s to %s, helo %s", 830 spamtrap ? "greytrap " : "", ip, from, to, helo); 831 } else { 832 /* existing entry */ 833 if (gdcopyin(&dbd, &gd) == -1) { 834 /* whatever this is, it doesn't belong */ 835 db->del(db, &dbk, 0); 836 db->sync(db, 0); 837 goto bad; 838 } 839 gd.bcount++; 840 gd.pcount = spamtrap ? -1 : 0; 841 if (gd.first + passtime < now) 842 gd.pass = now; 843 memset(&dbk, 0, sizeof(dbk)); 844 dbk.size = strlen(lookup); 845 dbk.data = lookup; 846 memset(&dbd, 0, sizeof(dbd)); 847 dbd.size = sizeof(gd); 848 dbd.data = &gd; 849 r = db->put(db, &dbk, &dbd, 0); 850 db->sync(db, 0); 851 if (r) 852 goto bad; 853 if (debug) 854 fprintf(stderr, "updated %s\n", lookup); 855 } 856 free(key); 857 key = NULL; 858 db->close(db); 859 db = NULL; 860 861 /* Entry successfully update, sent out sync message */ 862 if (syncsend && sync) { 863 if (spamtrap) { 864 syslog_r(LOG_DEBUG, &sdata, 865 "sync_trap %s", ip); 866 sync_trapped(now, now + expire, ip); 867 } 868 else 869 sync_update(now, helo, ip, from, to); 870 } 871 return(0); 872 bad: 873 free(key); 874 key = NULL; 875 db->close(db); 876 db = NULL; 877 return(-1); 878 } 879 880 int 881 twread(char *buf) 882 { 883 if ((strncmp(buf, "WHITE:", 6) == 0) || 884 (strncmp(buf, "TRAP:", 5) == 0)) { 885 char **ap, *argv[5]; 886 int argc = 0; 887 888 for (ap = argv; 889 ap < &argv[4] && (*ap = strsep(&buf, ":")) != NULL;) { 890 if (**ap != '\0') 891 ap++; 892 argc++; 893 } 894 *ap = NULL; 895 if (argc != 4) 896 return (-1); 897 twupdate(PATH_SPAMD_DB, argv[0], argv[1], argv[2], argv[3]); 898 return (0); 899 } else 900 return (-1); 901 } 902 903 int 904 greyreader(void) 905 { 906 char cip[32], ip[32], helo[MAX_MAIL], from[MAX_MAIL], to[MAX_MAIL]; 907 char *buf; 908 size_t len; 909 int state, sync; 910 struct addrinfo hints, *res; 911 912 memset(&hints, 0, sizeof(hints)); 913 hints.ai_family = AF_INET; /*for now*/ 914 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 915 hints.ai_protocol = IPPROTO_UDP; /*dummy*/ 916 hints.ai_flags = AI_NUMERICHOST; 917 918 state = 0; 919 sync = 1; 920 if (grey == NULL) { 921 syslog_r(LOG_ERR, &sdata, "No greylist pipe stream!\n"); 922 return (-1); 923 } 924 925 /* grab trap suffixes */ 926 readsuffixlists(); 927 928 while ((buf = fgetln(grey, &len))) { 929 if (buf[len - 1] == '\n') 930 buf[len - 1] = '\0'; 931 else 932 /* all valid lines end in \n */ 933 continue; 934 if (strlen(buf) < 4) 935 continue; 936 937 if (strcmp(buf, "SYNC") == 0) { 938 sync = 0; 939 continue; 940 } 941 942 switch (state) { 943 case 0: 944 if (twread(buf) == 0) { 945 state = 0; 946 break; 947 } 948 if (strncmp(buf, "HE:", 3) != 0) { 949 if (strncmp(buf, "CO:", 3) == 0) 950 strlcpy(cip, buf+3, sizeof(cip)); 951 state = 0; 952 break; 953 } 954 strlcpy(helo, buf+3, sizeof(helo)); 955 state = 1; 956 break; 957 case 1: 958 if (strncmp(buf, "IP:", 3) != 0) 959 break; 960 strlcpy(ip, buf+3, sizeof(ip)); 961 if (getaddrinfo(ip, NULL, &hints, &res) == 0) { 962 freeaddrinfo(res); 963 state = 2; 964 } else 965 state = 0; 966 break; 967 case 2: 968 if (strncmp(buf, "FR:", 3) != 0) { 969 state = 0; 970 break; 971 } 972 strlcpy(from, buf+3, sizeof(from)); 973 state = 3; 974 break; 975 case 3: 976 if (strncmp(buf, "TO:", 3) != 0) { 977 state = 0; 978 break; 979 } 980 strlcpy(to, buf+3, sizeof(to)); 981 if (debug) 982 fprintf(stderr, 983 "Got Grey HELO %s, IP %s from %s to %s\n", 984 helo, ip, from, to); 985 greyupdate(PATH_SPAMD_DB, helo, ip, from, to, sync, cip); 986 sync = 1; 987 state = 0; 988 break; 989 } 990 } 991 return (0); 992 } 993 994 void 995 greyscanner(void) 996 { 997 for (;;) { 998 if (greyscan(PATH_SPAMD_DB) == -1) 999 syslog_r(LOG_NOTICE, &sdata, "scan of %s failed", 1000 PATH_SPAMD_DB); 1001 sleep(DB_SCAN_INTERVAL); 1002 } 1003 } 1004 1005 static void 1006 drop_privs(void) 1007 { 1008 /* 1009 * lose root, continue as non-root user 1010 */ 1011 if (setgroups(1, &pw->pw_gid) || 1012 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 1013 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) { 1014 syslog_r(LOG_ERR, &sdata, "failed to drop privs (%m)"); 1015 exit(1); 1016 } 1017 } 1018 1019 void 1020 check_spamd_db(void) 1021 { 1022 HASHINFO hashinfo; 1023 int i = -1; 1024 DB *db; 1025 1026 /* check to see if /var/db/spamd exists, if not, create it */ 1027 memset(&hashinfo, 0, sizeof(hashinfo)); 1028 db = dbopen(PATH_SPAMD_DB, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 1029 1030 if (db == NULL) { 1031 switch (errno) { 1032 case ENOENT: 1033 i = open(PATH_SPAMD_DB, O_RDWR|O_CREAT, 0644); 1034 if (i == -1) { 1035 syslog_r(LOG_ERR, &sdata, 1036 "create %s failed (%m)", PATH_SPAMD_DB); 1037 exit(1); 1038 } 1039 /* if we are dropping privs, chown to that user */ 1040 if (pw && (fchown(i, pw->pw_uid, pw->pw_gid) == -1)) { 1041 syslog_r(LOG_ERR, &sdata, 1042 "chown %s failed (%m)", PATH_SPAMD_DB); 1043 exit(1); 1044 } 1045 close(i); 1046 return; 1047 break; 1048 default: 1049 syslog_r(LOG_ERR, &sdata, "open of %s failed (%m)", 1050 PATH_SPAMD_DB); 1051 exit(1); 1052 } 1053 } 1054 db->sync(db, 0); 1055 db->close(db); 1056 } 1057 1058 1059 int 1060 greywatcher(void) 1061 { 1062 struct sigaction sa; 1063 1064 drop_privs(); 1065 1066 if (pledge("stdio rpath wpath inet flock proc exec", NULL) == -1) { 1067 syslog_r(LOG_ERR, &sdata, "pledge failed (%m)"); 1068 exit(1); 1069 } 1070 1071 startup = time(NULL); 1072 db_pid = fork(); 1073 switch (db_pid) { 1074 case -1: 1075 syslog_r(LOG_ERR, &sdata, "fork failed (%m)"); 1076 exit(1); 1077 case 0: 1078 /* 1079 * child, talks to jailed spamd over greypipe, 1080 * updates db. has no access to pf. 1081 */ 1082 close(pfdev); 1083 setproctitle("(%s update)", PATH_SPAMD_DB); 1084 if (greyreader() == -1) { 1085 syslog_r(LOG_ERR, &sdata, "greyreader failed (%m)"); 1086 _exit(1); 1087 } 1088 _exit(0); 1089 } 1090 1091 1092 fclose(grey); 1093 /* 1094 * parent, scans db periodically for changes and updates 1095 * pf whitelist table accordingly. 1096 */ 1097 1098 sigfillset(&sa.sa_mask); 1099 sa.sa_flags = SA_RESTART; 1100 sa.sa_handler = sig_term_chld; 1101 sigaction(SIGTERM, &sa, NULL); 1102 sigaction(SIGHUP, &sa, NULL); 1103 sigaction(SIGCHLD, &sa, NULL); 1104 sigaction(SIGINT, &sa, NULL); 1105 1106 setproctitle("(pf <spamd-white> update)"); 1107 greyscanner(); 1108 exit(1); 1109 } 1110