1 /* $OpenBSD: grey.c,v 1.46 2009/02/25 19:00:36 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 if (buf[len-1] == '\n') 326 len--; 327 if ((m = malloc(sizeof(struct mail_addr))) == NULL) 328 goto bad; 329 if ((len + 1) > sizeof(m->addr)) { 330 syslog_r(LOG_ERR, &sdata, 331 "line too long in %s - file ignored", 332 alloweddomains_file); 333 goto bad; 334 } 335 memcpy(m->addr, buf, len); 336 m->addr[len]='\0'; 337 syslog_r(LOG_ERR, &sdata, "got suffix %s", m->addr); 338 SLIST_INSERT_HEAD(&match_suffix, m, entry); 339 } 340 } 341 return; 342 bad: 343 while (!SLIST_EMPTY(&match_suffix)) { 344 m = SLIST_FIRST(&match_suffix); 345 SLIST_REMOVE_HEAD(&match_suffix, entry); 346 free(m); 347 } 348 } 349 350 void 351 freeaddrlists(void) 352 { 353 int i; 354 355 if (whitelist != NULL) 356 for (i = 0; i < whitecount; i++) { 357 free(whitelist[i]); 358 whitelist[i] = NULL; 359 } 360 whitecount = 0; 361 if (traplist != NULL) { 362 for (i = 0; i < trapcount; i++) { 363 free(traplist[i]); 364 traplist[i] = NULL; 365 } 366 } 367 trapcount = 0; 368 } 369 370 /* validate, then add to list of addrs to whitelist */ 371 int 372 addwhiteaddr(char *addr) 373 { 374 struct addrinfo hints, *res; 375 376 memset(&hints, 0, sizeof(hints)); 377 hints.ai_family = AF_INET; /*for now*/ 378 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 379 hints.ai_protocol = IPPROTO_UDP; /*dummy*/ 380 hints.ai_flags = AI_NUMERICHOST; 381 382 if (getaddrinfo(addr, NULL, &hints, &res) == 0) { 383 if (whitecount == whitealloc) { 384 char **tmp; 385 386 tmp = realloc(whitelist, 387 (whitealloc + 1024) * sizeof(char *)); 388 if (tmp == NULL) { 389 freeaddrinfo(res); 390 return(-1); 391 } 392 whitelist = tmp; 393 whitealloc += 1024; 394 } 395 whitelist[whitecount] = strdup(addr); 396 if (whitelist[whitecount] == NULL) { 397 freeaddrinfo(res); 398 return(-1); 399 } 400 whitecount++; 401 freeaddrinfo(res); 402 } else 403 return(-1); 404 return(0); 405 } 406 407 /* validate, then add to list of addrs to traplist */ 408 int 409 addtrapaddr(char *addr) 410 { 411 struct addrinfo hints, *res; 412 413 memset(&hints, 0, sizeof(hints)); 414 hints.ai_family = AF_INET; /*for now*/ 415 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 416 hints.ai_protocol = IPPROTO_UDP; /*dummy*/ 417 hints.ai_flags = AI_NUMERICHOST; 418 419 if (getaddrinfo(addr, NULL, &hints, &res) == 0) { 420 if (trapcount == trapalloc) { 421 char **tmp; 422 423 tmp = realloc(traplist, 424 (trapalloc + 1024) * sizeof(char *)); 425 if (tmp == NULL) { 426 freeaddrinfo(res); 427 return(-1); 428 } 429 traplist = tmp; 430 trapalloc += 1024; 431 } 432 traplist[trapcount] = strdup(addr); 433 if (traplist[trapcount] == NULL) { 434 freeaddrinfo(res); 435 return(-1); 436 } 437 trapcount++; 438 freeaddrinfo(res); 439 } else 440 return(-1); 441 return(0); 442 } 443 444 static int 445 queue_change(char *key, char *data, size_t dsiz, int act) 446 { 447 struct db_change *dbc; 448 449 if ((dbc = malloc(sizeof(*dbc))) == NULL) { 450 syslog_r(LOG_DEBUG, &sdata, "malloc failed (queue change)"); 451 return(-1); 452 } 453 if ((dbc->key = strdup(key)) == NULL) { 454 syslog_r(LOG_DEBUG, &sdata, "malloc failed (queue change)"); 455 free(dbc); 456 return(-1); 457 } 458 if ((dbc->data = malloc(dsiz)) == NULL) { 459 syslog_r(LOG_DEBUG, &sdata, "malloc failed (queue change)"); 460 free(dbc->key); 461 free(dbc); 462 return(-1); 463 } 464 memcpy(dbc->data, data, dsiz); 465 dbc->dsiz = dsiz; 466 dbc->act = act; 467 syslog_r(LOG_DEBUG, &sdata, 468 "queueing %s of %s", ((act == DBC_ADD) ? "add" : "deletion"), 469 dbc->key); 470 SLIST_INSERT_HEAD(&db_changes, dbc, entry); 471 return(0); 472 } 473 474 static int 475 do_changes(DB *db) 476 { 477 DBT dbk, dbd; 478 struct db_change *dbc; 479 int ret = 0; 480 481 while (!SLIST_EMPTY(&db_changes)) { 482 dbc = SLIST_FIRST(&db_changes); 483 switch (dbc->act) { 484 case DBC_ADD: 485 memset(&dbk, 0, sizeof(dbk)); 486 dbk.size = strlen(dbc->key); 487 dbk.data = dbc->key; 488 memset(&dbd, 0, sizeof(dbd)); 489 dbd.size = dbc->dsiz; 490 dbd.data = dbc->data; 491 if (db->put(db, &dbk, &dbd, 0)) { 492 db->sync(db, 0); 493 syslog_r(LOG_ERR, &sdata, 494 "can't add %s to spamd db (%m)", dbc->key); 495 ret = -1; 496 } 497 db->sync(db, 0); 498 break; 499 case DBC_DEL: 500 memset(&dbk, 0, sizeof(dbk)); 501 dbk.size = strlen(dbc->key); 502 dbk.data = dbc->key; 503 if (db->del(db, &dbk, 0)) { 504 syslog_r(LOG_ERR, &sdata, 505 "can't delete %s from spamd db (%m)", 506 dbc->key); 507 ret = -1; 508 } 509 break; 510 default: 511 syslog_r(LOG_ERR, &sdata, "Unrecognized db change"); 512 ret = -1; 513 } 514 free(dbc->key); 515 dbc->key = NULL; 516 free(dbc->data); 517 dbc->data = NULL; 518 dbc->act = 0; 519 dbc->dsiz = 0; 520 SLIST_REMOVE_HEAD(&db_changes, entry); 521 free(dbc); 522 523 } 524 return(ret); 525 } 526 527 int 528 db_notin(DB *db, char *key) 529 { 530 int i; 531 DBT dbk, dbd; 532 533 memset(&dbk, 0, sizeof(dbk)); 534 dbk.size = strlen(key); 535 dbk.data = key; 536 memset(&dbd, 0, 537 sizeof(dbd)); 538 i = db->get(db, &dbk, &dbd, 0); 539 if (i == -1) 540 return (-1); 541 if (i) 542 /* not in the database */ 543 return (1); 544 else 545 /* it is in the database */ 546 return (0); 547 } 548 549 550 int 551 greyscan(char *dbname) 552 { 553 HASHINFO hashinfo; 554 DBT dbk, dbd; 555 DB *db; 556 struct gdata gd; 557 int r; 558 char *a = NULL; 559 size_t asiz = 0; 560 time_t now = time(NULL); 561 562 /* walk db, expire, and whitelist */ 563 memset(&hashinfo, 0, sizeof(hashinfo)); 564 db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 565 if (db == NULL) { 566 syslog_r(LOG_INFO, &sdata, "dbopen failed (%m)"); 567 return(-1); 568 } 569 memset(&dbk, 0, sizeof(dbk)); 570 memset(&dbd, 0, sizeof(dbd)); 571 for (r = db->seq(db, &dbk, &dbd, R_FIRST); !r; 572 r = db->seq(db, &dbk, &dbd, R_NEXT)) { 573 if ((dbk.size < 1) || dbd.size != sizeof(struct gdata)) { 574 syslog_r(LOG_ERR, &sdata, "bogus entry in spamd database"); 575 goto bad; 576 } 577 if (asiz < dbk.size + 1) { 578 char *tmp; 579 580 tmp = realloc(a, dbk.size * 2); 581 if (tmp == NULL) 582 goto bad; 583 a = tmp; 584 asiz = dbk.size * 2; 585 } 586 memset(a, 0, asiz); 587 memcpy(a, dbk.data, dbk.size); 588 memcpy(&gd, dbd.data, sizeof(gd)); 589 if (gd.expire <= now && gd.pcount != -2) { 590 /* get rid of entry */ 591 if (queue_change(a, NULL, 0, DBC_DEL) == -1) 592 goto bad; 593 } else if (gd.pcount == -1) { 594 /* this is a greytrap hit */ 595 if ((addtrapaddr(a) == -1) && 596 (queue_change(a, NULL, 0, DBC_DEL) == -1)) 597 goto bad; 598 } else if (gd.pcount >= 0 && gd.pass <= now) { 599 int tuple = 0; 600 char *cp; 601 602 /* 603 * add address to whitelist 604 * add an address-keyed entry to db 605 */ 606 cp = strchr(a, '\n'); 607 if (cp != NULL) { 608 tuple = 1; 609 *cp = '\0'; 610 } 611 612 if (addwhiteaddr(a) == -1) { 613 if (cp != NULL) 614 *cp = '\n'; 615 if (queue_change(a, NULL, 0, DBC_DEL) == -1) 616 goto bad; 617 } 618 619 if (tuple && db_notin(db, a)) { 620 if (cp != NULL) 621 *cp = '\0'; 622 /* re-add entry, keyed only by ip */ 623 gd.expire = now + whiteexp; 624 dbd.size = sizeof(gd); 625 dbd.data = &gd; 626 if (queue_change(a, (void *) &gd, sizeof(gd), 627 DBC_ADD) == -1) 628 goto bad; 629 syslog_r(LOG_DEBUG, &sdata, 630 "whitelisting %s in %s", a, dbname); 631 } 632 if (debug) 633 fprintf(stderr, "whitelisted %s\n", a); 634 } 635 } 636 (void) do_changes(db); 637 db->close(db); 638 db = NULL; 639 configure_pf(whitelist, whitecount); 640 configure_spamd(traplist, trapcount, trapcfg); 641 642 freeaddrlists(); 643 free(a); 644 a = NULL; 645 asiz = 0; 646 return(0); 647 bad: 648 (void) do_changes(db); 649 db->close(db); 650 db = NULL; 651 freeaddrlists(); 652 free(a); 653 a = NULL; 654 asiz = 0; 655 return(-1); 656 } 657 658 int 659 trapcheck(DB *db, char *to) 660 { 661 int i, j, smatch = 0; 662 DBT dbk, dbd; 663 struct mail_addr *m; 664 char * trap; 665 size_t s; 666 667 trap = dequotetolower(to); 668 if (!SLIST_EMPTY(&match_suffix)) { 669 s = strlen(trap); 670 SLIST_FOREACH(m, &match_suffix, entry) { 671 j = s - strlen(m->addr); 672 if ((j >= 0) && (strcasecmp(trap+j, m->addr) == 0)) 673 smatch = 1; 674 } 675 if (!smatch) 676 /* no suffixes match, so trap it */ 677 return (0); 678 } 679 memset(&dbk, 0, sizeof(dbk)); 680 dbk.size = strlen(trap); 681 dbk.data = trap; 682 memset(&dbd, 0, sizeof(dbd)); 683 i = db->get(db, &dbk, &dbd, 0); 684 if (i == -1) 685 return (-1); 686 if (i) 687 /* didn't exist - so this doesn't match a known spamtrap */ 688 return (1); 689 else 690 /* To: address is a spamtrap, so add as a greytrap entry */ 691 return (0); 692 } 693 694 int 695 twupdate(char *dbname, char *what, char *ip, char *source, char *expires) 696 { 697 /* we got a TRAP or WHITE update from someone else */ 698 HASHINFO hashinfo; 699 DBT dbk, dbd; 700 DB *db; 701 struct gdata gd; 702 time_t now, expire; 703 int r, spamtrap; 704 705 now = time(NULL); 706 /* expiry times have to be in the future */ 707 expire = strtonum(expires, now, INT_MAX, NULL); 708 if (expire == 0) 709 return(-1); 710 711 if (strcmp(what, "TRAP") == 0) 712 spamtrap = 1; 713 else if (strcmp(what, "WHITE") == 0) 714 spamtrap = 0; 715 else 716 return(-1); 717 718 memset(&hashinfo, 0, sizeof(hashinfo)); 719 db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 720 if (db == NULL) 721 return(-1); 722 723 memset(&dbk, 0, sizeof(dbk)); 724 dbk.size = strlen(ip); 725 dbk.data = ip; 726 memset(&dbd, 0, sizeof(dbd)); 727 r = db->get(db, &dbk, &dbd, 0); 728 if (r == -1) 729 goto bad; 730 if (r) { 731 /* new entry */ 732 memset(&gd, 0, sizeof(gd)); 733 gd.first = now; 734 gd.pcount = spamtrap ? -1 : 0; 735 gd.expire = expire; 736 memset(&dbk, 0, sizeof(dbk)); 737 dbk.size = strlen(ip); 738 dbk.data = ip; 739 memset(&dbd, 0, sizeof(dbd)); 740 dbd.size = sizeof(gd); 741 dbd.data = &gd; 742 r = db->put(db, &dbk, &dbd, 0); 743 db->sync(db, 0); 744 if (r) 745 goto bad; 746 if (debug) 747 fprintf(stderr, "added %s %s\n", 748 spamtrap ? "trap entry for" : "", ip); 749 syslog_r(LOG_DEBUG, &sdata, 750 "new %s from %s for %s, expires %s", what, source, ip, 751 expires); 752 } else { 753 /* existing entry */ 754 if (dbd.size != sizeof(gd)) { 755 /* whatever this is, it doesn't belong */ 756 db->del(db, &dbk, 0); 757 db->sync(db, 0); 758 goto bad; 759 } 760 memcpy(&gd, dbd.data, sizeof(gd)); 761 if (spamtrap) { 762 gd.pcount = -1; 763 gd.bcount++; 764 } else 765 gd.pcount++; 766 memset(&dbk, 0, sizeof(dbk)); 767 dbk.size = strlen(ip); 768 dbk.data = ip; 769 memset(&dbd, 0, sizeof(dbd)); 770 dbd.size = sizeof(gd); 771 dbd.data = &gd; 772 r = db->put(db, &dbk, &dbd, 0); 773 db->sync(db, 0); 774 if (r) 775 goto bad; 776 if (debug) 777 fprintf(stderr, "updated %s\n", ip); 778 } 779 db->close(db); 780 return(0); 781 bad: 782 db->close(db); 783 return(-1); 784 785 } 786 787 int 788 greyupdate(char *dbname, char *helo, char *ip, char *from, char *to, int sync, 789 char *cip) 790 { 791 HASHINFO hashinfo; 792 DBT dbk, dbd; 793 DB *db; 794 char *key = NULL; 795 char *lookup; 796 struct gdata gd; 797 time_t now, expire; 798 int r, spamtrap; 799 800 now = time(NULL); 801 802 /* open with lock, find record, update, close, unlock */ 803 memset(&hashinfo, 0, sizeof(hashinfo)); 804 db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 805 if (db == NULL) 806 return(-1); 807 if (asprintf(&key, "%s\n%s\n%s\n%s", ip, helo, from, to) == -1) 808 goto bad; 809 r = trapcheck(db, to); 810 switch (r) { 811 case 1: 812 /* do not trap */ 813 spamtrap = 0; 814 lookup = key; 815 expire = greyexp; 816 break; 817 case 0: 818 /* trap */ 819 spamtrap = 1; 820 lookup = ip; 821 expire = trapexp; 822 syslog_r(LOG_DEBUG, &sdata, "Trapping %s for tuple %s", ip, 823 key); 824 break; 825 default: 826 goto bad; 827 break; 828 } 829 memset(&dbk, 0, sizeof(dbk)); 830 dbk.size = strlen(lookup); 831 dbk.data = lookup; 832 memset(&dbd, 0, sizeof(dbd)); 833 r = db->get(db, &dbk, &dbd, 0); 834 if (r == -1) 835 goto bad; 836 if (r) { 837 /* new entry */ 838 if (sync && low_prio_mx_ip && 839 (strcmp(cip, low_prio_mx_ip) == 0) && 840 ((startup + 60) < now)) { 841 /* we haven't seen a greylist entry for this tuple, 842 * and yet the connection was to a low priority MX 843 * which we know can't be hit first if the client 844 * is adhering to the RFC's - soo.. kill it! 845 */ 846 spamtrap = 1; 847 lookup = ip; 848 expire = trapexp; 849 syslog_r(LOG_DEBUG, &sdata, 850 "Trapping %s for trying %s first for tuple %s", 851 ip, low_prio_mx_ip, key); 852 } 853 memset(&gd, 0, sizeof(gd)); 854 gd.first = now; 855 gd.bcount = 1; 856 gd.pcount = spamtrap ? -1 : 0; 857 gd.pass = now + expire; 858 gd.expire = now + expire; 859 memset(&dbk, 0, sizeof(dbk)); 860 dbk.size = strlen(lookup); 861 dbk.data = lookup; 862 memset(&dbd, 0, sizeof(dbd)); 863 dbd.size = sizeof(gd); 864 dbd.data = &gd; 865 r = db->put(db, &dbk, &dbd, 0); 866 db->sync(db, 0); 867 if (r) 868 goto bad; 869 if (debug) 870 fprintf(stderr, "added %s %s\n", 871 spamtrap ? "greytrap entry for" : "", lookup); 872 syslog_r(LOG_DEBUG, &sdata, 873 "new %sentry %s from %s to %s, helo %s", 874 spamtrap ? "greytrap " : "", ip, from, to, helo); 875 } else { 876 /* existing entry */ 877 if (dbd.size != sizeof(gd)) { 878 /* whatever this is, it doesn't belong */ 879 db->del(db, &dbk, 0); 880 db->sync(db, 0); 881 goto bad; 882 } 883 memcpy(&gd, dbd.data, sizeof(gd)); 884 gd.bcount++; 885 gd.pcount = spamtrap ? -1 : 0; 886 if (gd.first + passtime < now) 887 gd.pass = now; 888 memset(&dbk, 0, sizeof(dbk)); 889 dbk.size = strlen(lookup); 890 dbk.data = lookup; 891 memset(&dbd, 0, sizeof(dbd)); 892 dbd.size = sizeof(gd); 893 dbd.data = &gd; 894 r = db->put(db, &dbk, &dbd, 0); 895 db->sync(db, 0); 896 if (r) 897 goto bad; 898 if (debug) 899 fprintf(stderr, "updated %s\n", lookup); 900 } 901 free(key); 902 key = NULL; 903 db->close(db); 904 db = NULL; 905 906 /* Entry successfully update, sent out sync message */ 907 if (syncsend && sync) { 908 if (spamtrap) { 909 syslog_r(LOG_DEBUG, &sdata, 910 "sync_trap %s", ip); 911 sync_trapped(now, now + expire, ip); 912 } 913 else 914 sync_update(now, helo, ip, from, to); 915 } 916 return(0); 917 bad: 918 free(key); 919 key = NULL; 920 db->close(db); 921 db = NULL; 922 return(-1); 923 } 924 925 int 926 twread(char *buf) 927 { 928 if ((strncmp(buf, "WHITE:", 6) == 0) || 929 (strncmp(buf, "TRAP:", 5) == 0)) { 930 char **ap, *argv[5]; 931 int argc = 0; 932 933 for (ap = argv; 934 ap < &argv[4] && (*ap = strsep(&buf, ":")) != NULL;) { 935 if (**ap != '\0') 936 ap++; 937 argc++; 938 } 939 *ap = NULL; 940 if (argc != 4) 941 return (-1); 942 twupdate(PATH_SPAMD_DB, argv[0], argv[1], argv[2], argv[3]); 943 return (0); 944 } else 945 return (-1); 946 } 947 948 int 949 greyreader(void) 950 { 951 char cip[32], ip[32], helo[MAX_MAIL], from[MAX_MAIL], to[MAX_MAIL]; 952 char *buf; 953 size_t len; 954 int state, sync; 955 struct addrinfo hints, *res; 956 957 memset(&hints, 0, sizeof(hints)); 958 hints.ai_family = AF_INET; /*for now*/ 959 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 960 hints.ai_protocol = IPPROTO_UDP; /*dummy*/ 961 hints.ai_flags = AI_NUMERICHOST; 962 963 state = 0; 964 sync = 1; 965 if (grey == NULL) { 966 syslog_r(LOG_ERR, &sdata, "No greylist pipe stream!\n"); 967 exit(1); 968 } 969 970 /* grab trap suffixes */ 971 readsuffixlists(); 972 973 while ((buf = fgetln(grey, &len))) { 974 if (buf[len - 1] == '\n') 975 buf[len - 1] = '\0'; 976 else 977 /* all valid lines end in \n */ 978 continue; 979 if (strlen(buf) < 4) 980 continue; 981 982 if (strcmp(buf, "SYNC") == 0) { 983 sync = 0; 984 continue; 985 } 986 987 switch (state) { 988 case 0: 989 if (twread(buf) == 0) { 990 state = 0; 991 break; 992 } 993 if (strncmp(buf, "HE:", 3) != 0) { 994 if (strncmp(buf, "CO:", 3) == 0) 995 strlcpy(cip, buf+3, sizeof(cip)); 996 state = 0; 997 break; 998 } 999 strlcpy(helo, buf+3, sizeof(helo)); 1000 state = 1; 1001 break; 1002 case 1: 1003 if (strncmp(buf, "IP:", 3) != 0) 1004 break; 1005 strlcpy(ip, buf+3, sizeof(ip)); 1006 if (getaddrinfo(ip, NULL, &hints, &res) == 0) { 1007 freeaddrinfo(res); 1008 state = 2; 1009 } else 1010 state = 0; 1011 break; 1012 case 2: 1013 if (strncmp(buf, "FR:", 3) != 0) { 1014 state = 0; 1015 break; 1016 } 1017 strlcpy(from, buf+3, sizeof(from)); 1018 state = 3; 1019 break; 1020 case 3: 1021 if (strncmp(buf, "TO:", 3) != 0) { 1022 state = 0; 1023 break; 1024 } 1025 strlcpy(to, buf+3, sizeof(to)); 1026 if (debug) 1027 fprintf(stderr, 1028 "Got Grey HELO %s, IP %s from %s to %s\n", 1029 helo, ip, from, to); 1030 greyupdate(PATH_SPAMD_DB, helo, ip, from, to, sync, cip); 1031 sync = 1; 1032 state = 0; 1033 break; 1034 } 1035 } 1036 return (0); 1037 } 1038 1039 void 1040 greyscanner(void) 1041 { 1042 for (;;) { 1043 if (greyscan(PATH_SPAMD_DB) == -1) 1044 syslog_r(LOG_NOTICE, &sdata, "scan of %s failed", 1045 PATH_SPAMD_DB); 1046 sleep(DB_SCAN_INTERVAL); 1047 } 1048 /* NOTREACHED */ 1049 } 1050 1051 static void 1052 drop_privs(void) 1053 { 1054 /* 1055 * lose root, continue as non-root user 1056 */ 1057 if (pw) { 1058 setgroups(1, &pw->pw_gid); 1059 setegid(pw->pw_gid); 1060 setgid(pw->pw_gid); 1061 seteuid(pw->pw_uid); 1062 setuid(pw->pw_uid); 1063 } 1064 } 1065 1066 static void 1067 convert_spamd_db(void) 1068 { 1069 char sfn[] = "/var/db/spamd.XXXXXXXXX"; 1070 int r, fd = -1; 1071 DB *db1, *db2; 1072 BTREEINFO btreeinfo; 1073 HASHINFO hashinfo; 1074 DBT dbk, dbd; 1075 1076 /* try to open the db as a BTREE */ 1077 memset(&btreeinfo, 0, sizeof(btreeinfo)); 1078 db1 = dbopen(PATH_SPAMD_DB, O_EXLOCK|O_RDWR, 0600, DB_BTREE, 1079 &btreeinfo); 1080 if (db1 == NULL) { 1081 syslog_r(LOG_ERR, &sdata, 1082 "corrupt db in %s, remove and restart", PATH_SPAMD_DB); 1083 exit(1); 1084 } 1085 1086 if ((fd = mkstemp(sfn)) == -1) { 1087 syslog_r(LOG_ERR, &sdata, 1088 "can't convert %s: mkstemp failed (%m)", PATH_SPAMD_DB); 1089 exit(1); 1090 } 1091 memset(&hashinfo, 0, sizeof(hashinfo)); 1092 db2 = dbopen(sfn, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 1093 if (db2 == NULL) { 1094 unlink(sfn); 1095 syslog_r(LOG_ERR, &sdata, 1096 "can't convert %s: can't dbopen %s (%m)", PATH_SPAMD_DB, 1097 sfn); 1098 db1->close(db1); 1099 exit(1); 1100 } 1101 1102 memset(&dbk, 0, sizeof(dbk)); 1103 memset(&dbd, 0, sizeof(dbd)); 1104 for (r = db1->seq(db1, &dbk, &dbd, R_FIRST); !r; 1105 r = db1->seq(db1, &dbk, &dbd, R_NEXT)) { 1106 if (db2->put(db2, &dbk, &dbd, 0)) { 1107 db2->sync(db2, 0); 1108 db2->close(db2); 1109 db1->close(db1); 1110 unlink(sfn); 1111 syslog_r(LOG_ERR, &sdata, 1112 "can't convert %s - remove and restart", 1113 PATH_SPAMD_DB); 1114 exit(1); 1115 } 1116 } 1117 db2->sync(db2, 0); 1118 db2->close(db2); 1119 db1->sync(db1, 0); 1120 db1->close(db1); 1121 rename(sfn, PATH_SPAMD_DB); 1122 close(fd); 1123 /* if we are dropping privs, chown to that user */ 1124 if (pw && (chown(PATH_SPAMD_DB, pw->pw_uid, pw->pw_gid) == -1)) { 1125 syslog_r(LOG_ERR, &sdata, 1126 "chown %s failed (%m)", PATH_SPAMD_DB); 1127 exit(1); 1128 } 1129 } 1130 1131 static void 1132 check_spamd_db(void) 1133 { 1134 HASHINFO hashinfo; 1135 int i = -1; 1136 DB *db; 1137 1138 /* check to see if /var/db/spamd exists, if not, create it */ 1139 memset(&hashinfo, 0, sizeof(hashinfo)); 1140 db = dbopen(PATH_SPAMD_DB, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 1141 1142 if (db == NULL) { 1143 switch (errno) { 1144 case ENOENT: 1145 i = open(PATH_SPAMD_DB, O_RDWR|O_CREAT, 0644); 1146 if (i == -1) { 1147 syslog_r(LOG_ERR, &sdata, 1148 "create %s failed (%m)", PATH_SPAMD_DB); 1149 exit(1); 1150 } 1151 /* if we are dropping privs, chown to that user */ 1152 if (pw && (fchown(i, pw->pw_uid, pw->pw_gid) == -1)) { 1153 syslog_r(LOG_ERR, &sdata, 1154 "chown %s failed (%m)", PATH_SPAMD_DB); 1155 exit(1); 1156 } 1157 close(i); 1158 drop_privs(); 1159 return; 1160 break; 1161 case EFTYPE: 1162 /* 1163 * db may be old BTREE instead of HASH, attempt to 1164 * convert. 1165 */ 1166 convert_spamd_db(); 1167 drop_privs(); 1168 return; 1169 break; 1170 default: 1171 syslog_r(LOG_ERR, &sdata, "open of %s failed (%m)", 1172 PATH_SPAMD_DB); 1173 exit(1); 1174 } 1175 } 1176 db->sync(db, 0); 1177 db->close(db); 1178 drop_privs(); 1179 } 1180 1181 1182 int 1183 greywatcher(void) 1184 { 1185 struct sigaction sa; 1186 1187 check_spamd_db(); 1188 1189 startup = time(NULL); 1190 db_pid = fork(); 1191 switch (db_pid) { 1192 case -1: 1193 syslog_r(LOG_ERR, &sdata, "fork failed (%m)"); 1194 exit(1); 1195 case 0: 1196 /* 1197 * child, talks to jailed spamd over greypipe, 1198 * updates db. has no access to pf. 1199 */ 1200 close(pfdev); 1201 setproctitle("(%s update)", PATH_SPAMD_DB); 1202 greyreader(); 1203 syslog_r(LOG_ERR, &sdata, "greyreader failed (%m)"); 1204 /* NOTREACHED */ 1205 _exit(1); 1206 } 1207 1208 1209 fclose(grey); 1210 /* 1211 * parent, scans db periodically for changes and updates 1212 * pf whitelist table accordingly. 1213 */ 1214 1215 sigfillset(&sa.sa_mask); 1216 sa.sa_flags = SA_RESTART; 1217 sa.sa_handler = sig_term_chld; 1218 sigaction(SIGTERM, &sa, NULL); 1219 sigaction(SIGHUP, &sa, NULL); 1220 sigaction(SIGCHLD, &sa, NULL); 1221 sigaction(SIGINT, &sa, NULL); 1222 1223 setproctitle("(pf <spamd-white> update)"); 1224 greyscanner(); 1225 /* NOTREACHED */ 1226 exit(1); 1227 } 1228