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