1 /* $OpenBSD: grey.c,v 1.51 2011/03/03 21:58:58 deraadt 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 /* -1=error, 0=notfound, 1=TRAPPED, 2=WHITE */ 540 int 541 db_addrstate(DB *db, char *key) 542 { 543 int i; 544 DBT dbk, dbd; 545 struct gdata gd; 546 547 memset(&dbk, 0, sizeof(dbk)); 548 dbk.size = strlen(key); 549 dbk.data = key; 550 memset(&dbd, 0, sizeof(dbd)); 551 i = db->get(db, &dbk, &dbd, 0); 552 if (i == -1) 553 return (-1); 554 if (i) 555 /* not in the database */ 556 return (0); 557 memcpy(&gd, dbd.data, sizeof(gd)); 558 return gd.pcount == -1 ? 1 : 2; 559 } 560 561 562 int 563 greyscan(char *dbname) 564 { 565 HASHINFO hashinfo; 566 DBT dbk, dbd; 567 DB *db; 568 struct gdata gd; 569 int r; 570 char *a = NULL; 571 size_t asiz = 0; 572 time_t now = time(NULL); 573 574 /* walk db, expire, and whitelist */ 575 memset(&hashinfo, 0, sizeof(hashinfo)); 576 db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 577 if (db == NULL) { 578 syslog_r(LOG_INFO, &sdata, "dbopen failed (%m)"); 579 return(-1); 580 } 581 memset(&dbk, 0, sizeof(dbk)); 582 memset(&dbd, 0, sizeof(dbd)); 583 for (r = db->seq(db, &dbk, &dbd, R_FIRST); !r; 584 r = db->seq(db, &dbk, &dbd, R_NEXT)) { 585 if ((dbk.size < 1) || dbd.size != sizeof(struct gdata)) { 586 syslog_r(LOG_ERR, &sdata, "bogus entry in spamd database"); 587 goto bad; 588 } 589 if (asiz < dbk.size + 1) { 590 char *tmp; 591 592 tmp = realloc(a, dbk.size * 2); 593 if (tmp == NULL) 594 goto bad; 595 a = tmp; 596 asiz = dbk.size * 2; 597 } 598 memset(a, 0, asiz); 599 memcpy(a, dbk.data, dbk.size); 600 memcpy(&gd, dbd.data, sizeof(gd)); 601 if (gd.expire <= now && gd.pcount != -2) { 602 /* get rid of entry */ 603 if (queue_change(a, NULL, 0, DBC_DEL) == -1) 604 goto bad; 605 } else if (gd.pcount == -1) { 606 /* this is a greytrap hit */ 607 if ((addtrapaddr(a) == -1) && 608 (queue_change(a, NULL, 0, DBC_DEL) == -1)) 609 goto bad; 610 } else if (gd.pcount >= 0 && gd.pass <= now) { 611 int tuple = 0; 612 char *cp; 613 int state; 614 615 /* 616 * if not already TRAPPED, 617 * add address to whitelist 618 * add an address-keyed entry to db 619 */ 620 cp = strchr(a, '\n'); 621 if (cp != NULL) { 622 tuple = 1; 623 *cp = '\0'; 624 } 625 626 state = db_addrstate(db, a); 627 if (state != 1 && addwhiteaddr(a) == -1) { 628 if (cp != NULL) 629 *cp = '\n'; 630 if (queue_change(a, NULL, 0, DBC_DEL) == -1) 631 goto bad; 632 } 633 634 if (tuple && state <= 0) { 635 if (cp != NULL) 636 *cp = '\0'; 637 /* re-add entry, keyed only by ip */ 638 gd.expire = now + whiteexp; 639 dbd.size = sizeof(gd); 640 dbd.data = &gd; 641 if (queue_change(a, (void *) &gd, sizeof(gd), 642 DBC_ADD) == -1) 643 goto bad; 644 syslog_r(LOG_DEBUG, &sdata, 645 "whitelisting %s in %s", a, dbname); 646 } 647 if (debug) 648 fprintf(stderr, "whitelisted %s\n", a); 649 } 650 } 651 (void) do_changes(db); 652 db->close(db); 653 db = NULL; 654 configure_pf(whitelist, whitecount); 655 configure_spamd(traplist, trapcount, trapcfg); 656 657 freeaddrlists(); 658 free(a); 659 a = NULL; 660 asiz = 0; 661 return(0); 662 bad: 663 (void) do_changes(db); 664 db->close(db); 665 db = NULL; 666 freeaddrlists(); 667 free(a); 668 a = NULL; 669 asiz = 0; 670 return(-1); 671 } 672 673 int 674 trapcheck(DB *db, char *to) 675 { 676 int i, j, smatch = 0; 677 DBT dbk, dbd; 678 struct mail_addr *m; 679 char * trap; 680 size_t s; 681 682 trap = dequotetolower(to); 683 if (!SLIST_EMPTY(&match_suffix)) { 684 s = strlen(trap); 685 SLIST_FOREACH(m, &match_suffix, entry) { 686 j = s - strlen(m->addr); 687 if ((j >= 0) && (strcasecmp(trap+j, m->addr) == 0)) 688 smatch = 1; 689 } 690 if (!smatch) 691 /* no suffixes match, so trap it */ 692 return (0); 693 } 694 memset(&dbk, 0, sizeof(dbk)); 695 dbk.size = strlen(trap); 696 dbk.data = trap; 697 memset(&dbd, 0, sizeof(dbd)); 698 i = db->get(db, &dbk, &dbd, 0); 699 if (i == -1) 700 return (-1); 701 if (i) 702 /* didn't exist - so this doesn't match a known spamtrap */ 703 return (1); 704 else 705 /* To: address is a spamtrap, so add as a greytrap entry */ 706 return (0); 707 } 708 709 int 710 twupdate(char *dbname, char *what, char *ip, char *source, char *expires) 711 { 712 /* we got a TRAP or WHITE update from someone else */ 713 HASHINFO hashinfo; 714 DBT dbk, dbd; 715 DB *db; 716 struct gdata gd; 717 time_t now, expire; 718 int r, spamtrap; 719 720 now = time(NULL); 721 /* expiry times have to be in the future */ 722 expire = strtonum(expires, now, INT_MAX, NULL); 723 if (expire == 0) 724 return(-1); 725 726 if (strcmp(what, "TRAP") == 0) 727 spamtrap = 1; 728 else if (strcmp(what, "WHITE") == 0) 729 spamtrap = 0; 730 else 731 return(-1); 732 733 memset(&hashinfo, 0, sizeof(hashinfo)); 734 db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 735 if (db == NULL) 736 return(-1); 737 738 memset(&dbk, 0, sizeof(dbk)); 739 dbk.size = strlen(ip); 740 dbk.data = ip; 741 memset(&dbd, 0, sizeof(dbd)); 742 r = db->get(db, &dbk, &dbd, 0); 743 if (r == -1) 744 goto bad; 745 if (r) { 746 /* new entry */ 747 memset(&gd, 0, sizeof(gd)); 748 gd.first = now; 749 gd.pcount = spamtrap ? -1 : 0; 750 gd.expire = expire; 751 memset(&dbk, 0, sizeof(dbk)); 752 dbk.size = strlen(ip); 753 dbk.data = ip; 754 memset(&dbd, 0, sizeof(dbd)); 755 dbd.size = sizeof(gd); 756 dbd.data = &gd; 757 r = db->put(db, &dbk, &dbd, 0); 758 db->sync(db, 0); 759 if (r) 760 goto bad; 761 if (debug) 762 fprintf(stderr, "added %s %s\n", 763 spamtrap ? "trap entry for" : "", ip); 764 syslog_r(LOG_DEBUG, &sdata, 765 "new %s from %s for %s, expires %s", what, source, ip, 766 expires); 767 } else { 768 /* existing entry */ 769 if (dbd.size != sizeof(gd)) { 770 /* whatever this is, it doesn't belong */ 771 db->del(db, &dbk, 0); 772 db->sync(db, 0); 773 goto bad; 774 } 775 memcpy(&gd, dbd.data, sizeof(gd)); 776 if (spamtrap) { 777 gd.pcount = -1; 778 gd.bcount++; 779 } else 780 gd.pcount++; 781 memset(&dbk, 0, sizeof(dbk)); 782 dbk.size = strlen(ip); 783 dbk.data = ip; 784 memset(&dbd, 0, sizeof(dbd)); 785 dbd.size = sizeof(gd); 786 dbd.data = &gd; 787 r = db->put(db, &dbk, &dbd, 0); 788 db->sync(db, 0); 789 if (r) 790 goto bad; 791 if (debug) 792 fprintf(stderr, "updated %s\n", ip); 793 } 794 db->close(db); 795 return(0); 796 bad: 797 db->close(db); 798 return(-1); 799 800 } 801 802 int 803 greyupdate(char *dbname, char *helo, char *ip, char *from, char *to, int sync, 804 char *cip) 805 { 806 HASHINFO hashinfo; 807 DBT dbk, dbd; 808 DB *db; 809 char *key = NULL; 810 char *lookup; 811 struct gdata gd; 812 time_t now, expire; 813 int r, spamtrap; 814 815 now = time(NULL); 816 817 /* open with lock, find record, update, close, unlock */ 818 memset(&hashinfo, 0, sizeof(hashinfo)); 819 db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 820 if (db == NULL) 821 return(-1); 822 if (asprintf(&key, "%s\n%s\n%s\n%s", ip, helo, from, to) == -1) 823 goto bad; 824 r = trapcheck(db, to); 825 switch (r) { 826 case 1: 827 /* do not trap */ 828 spamtrap = 0; 829 lookup = key; 830 expire = greyexp; 831 break; 832 case 0: 833 /* trap */ 834 spamtrap = 1; 835 lookup = ip; 836 expire = trapexp; 837 syslog_r(LOG_DEBUG, &sdata, "Trapping %s for tuple %s", ip, 838 key); 839 break; 840 default: 841 goto bad; 842 break; 843 } 844 memset(&dbk, 0, sizeof(dbk)); 845 dbk.size = strlen(lookup); 846 dbk.data = lookup; 847 memset(&dbd, 0, sizeof(dbd)); 848 r = db->get(db, &dbk, &dbd, 0); 849 if (r == -1) 850 goto bad; 851 if (r) { 852 /* new entry */ 853 if (sync && low_prio_mx_ip && 854 (strcmp(cip, low_prio_mx_ip) == 0) && 855 ((startup + 60) < now)) { 856 /* we haven't seen a greylist entry for this tuple, 857 * and yet the connection was to a low priority MX 858 * which we know can't be hit first if the client 859 * is adhering to the RFC's - soo.. kill it! 860 */ 861 spamtrap = 1; 862 lookup = ip; 863 expire = trapexp; 864 syslog_r(LOG_DEBUG, &sdata, 865 "Trapping %s for trying %s first for tuple %s", 866 ip, low_prio_mx_ip, key); 867 } 868 memset(&gd, 0, sizeof(gd)); 869 gd.first = now; 870 gd.bcount = 1; 871 gd.pcount = spamtrap ? -1 : 0; 872 gd.pass = now + expire; 873 gd.expire = now + expire; 874 memset(&dbk, 0, sizeof(dbk)); 875 dbk.size = strlen(lookup); 876 dbk.data = lookup; 877 memset(&dbd, 0, sizeof(dbd)); 878 dbd.size = sizeof(gd); 879 dbd.data = &gd; 880 r = db->put(db, &dbk, &dbd, 0); 881 db->sync(db, 0); 882 if (r) 883 goto bad; 884 if (debug) 885 fprintf(stderr, "added %s %s\n", 886 spamtrap ? "greytrap entry for" : "", lookup); 887 syslog_r(LOG_DEBUG, &sdata, 888 "new %sentry %s from %s to %s, helo %s", 889 spamtrap ? "greytrap " : "", ip, from, to, helo); 890 } else { 891 /* existing entry */ 892 if (dbd.size != sizeof(gd)) { 893 /* whatever this is, it doesn't belong */ 894 db->del(db, &dbk, 0); 895 db->sync(db, 0); 896 goto bad; 897 } 898 memcpy(&gd, dbd.data, sizeof(gd)); 899 gd.bcount++; 900 gd.pcount = spamtrap ? -1 : 0; 901 if (gd.first + passtime < now) 902 gd.pass = now; 903 memset(&dbk, 0, sizeof(dbk)); 904 dbk.size = strlen(lookup); 905 dbk.data = lookup; 906 memset(&dbd, 0, sizeof(dbd)); 907 dbd.size = sizeof(gd); 908 dbd.data = &gd; 909 r = db->put(db, &dbk, &dbd, 0); 910 db->sync(db, 0); 911 if (r) 912 goto bad; 913 if (debug) 914 fprintf(stderr, "updated %s\n", lookup); 915 } 916 free(key); 917 key = NULL; 918 db->close(db); 919 db = NULL; 920 921 /* Entry successfully update, sent out sync message */ 922 if (syncsend && sync) { 923 if (spamtrap) { 924 syslog_r(LOG_DEBUG, &sdata, 925 "sync_trap %s", ip); 926 sync_trapped(now, now + expire, ip); 927 } 928 else 929 sync_update(now, helo, ip, from, to); 930 } 931 return(0); 932 bad: 933 free(key); 934 key = NULL; 935 db->close(db); 936 db = NULL; 937 return(-1); 938 } 939 940 int 941 twread(char *buf) 942 { 943 if ((strncmp(buf, "WHITE:", 6) == 0) || 944 (strncmp(buf, "TRAP:", 5) == 0)) { 945 char **ap, *argv[5]; 946 int argc = 0; 947 948 for (ap = argv; 949 ap < &argv[4] && (*ap = strsep(&buf, ":")) != NULL;) { 950 if (**ap != '\0') 951 ap++; 952 argc++; 953 } 954 *ap = NULL; 955 if (argc != 4) 956 return (-1); 957 twupdate(PATH_SPAMD_DB, argv[0], argv[1], argv[2], argv[3]); 958 return (0); 959 } else 960 return (-1); 961 } 962 963 int 964 greyreader(void) 965 { 966 char cip[32], ip[32], helo[MAX_MAIL], from[MAX_MAIL], to[MAX_MAIL]; 967 char *buf; 968 size_t len; 969 int state, sync; 970 struct addrinfo hints, *res; 971 972 memset(&hints, 0, sizeof(hints)); 973 hints.ai_family = AF_INET; /*for now*/ 974 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 975 hints.ai_protocol = IPPROTO_UDP; /*dummy*/ 976 hints.ai_flags = AI_NUMERICHOST; 977 978 state = 0; 979 sync = 1; 980 if (grey == NULL) { 981 syslog_r(LOG_ERR, &sdata, "No greylist pipe stream!\n"); 982 exit(1); 983 } 984 985 /* grab trap suffixes */ 986 readsuffixlists(); 987 988 while ((buf = fgetln(grey, &len))) { 989 if (buf[len - 1] == '\n') 990 buf[len - 1] = '\0'; 991 else 992 /* all valid lines end in \n */ 993 continue; 994 if (strlen(buf) < 4) 995 continue; 996 997 if (strcmp(buf, "SYNC") == 0) { 998 sync = 0; 999 continue; 1000 } 1001 1002 switch (state) { 1003 case 0: 1004 if (twread(buf) == 0) { 1005 state = 0; 1006 break; 1007 } 1008 if (strncmp(buf, "HE:", 3) != 0) { 1009 if (strncmp(buf, "CO:", 3) == 0) 1010 strlcpy(cip, buf+3, sizeof(cip)); 1011 state = 0; 1012 break; 1013 } 1014 strlcpy(helo, buf+3, sizeof(helo)); 1015 state = 1; 1016 break; 1017 case 1: 1018 if (strncmp(buf, "IP:", 3) != 0) 1019 break; 1020 strlcpy(ip, buf+3, sizeof(ip)); 1021 if (getaddrinfo(ip, NULL, &hints, &res) == 0) { 1022 freeaddrinfo(res); 1023 state = 2; 1024 } else 1025 state = 0; 1026 break; 1027 case 2: 1028 if (strncmp(buf, "FR:", 3) != 0) { 1029 state = 0; 1030 break; 1031 } 1032 strlcpy(from, buf+3, sizeof(from)); 1033 state = 3; 1034 break; 1035 case 3: 1036 if (strncmp(buf, "TO:", 3) != 0) { 1037 state = 0; 1038 break; 1039 } 1040 strlcpy(to, buf+3, sizeof(to)); 1041 if (debug) 1042 fprintf(stderr, 1043 "Got Grey HELO %s, IP %s from %s to %s\n", 1044 helo, ip, from, to); 1045 greyupdate(PATH_SPAMD_DB, helo, ip, from, to, sync, cip); 1046 sync = 1; 1047 state = 0; 1048 break; 1049 } 1050 } 1051 return (0); 1052 } 1053 1054 void 1055 greyscanner(void) 1056 { 1057 for (;;) { 1058 if (greyscan(PATH_SPAMD_DB) == -1) 1059 syslog_r(LOG_NOTICE, &sdata, "scan of %s failed", 1060 PATH_SPAMD_DB); 1061 sleep(DB_SCAN_INTERVAL); 1062 } 1063 /* NOTREACHED */ 1064 } 1065 1066 static void 1067 drop_privs(void) 1068 { 1069 /* 1070 * lose root, continue as non-root user 1071 */ 1072 if (pw) { 1073 setgroups(1, &pw->pw_gid); 1074 setegid(pw->pw_gid); 1075 setgid(pw->pw_gid); 1076 seteuid(pw->pw_uid); 1077 setuid(pw->pw_uid); 1078 } 1079 } 1080 1081 static void 1082 convert_spamd_db(void) 1083 { 1084 char sfn[] = "/var/db/spamd.XXXXXXXXX"; 1085 int r, fd = -1; 1086 DB *db1, *db2; 1087 BTREEINFO btreeinfo; 1088 HASHINFO hashinfo; 1089 DBT dbk, dbd; 1090 1091 /* try to open the db as a BTREE */ 1092 memset(&btreeinfo, 0, sizeof(btreeinfo)); 1093 db1 = dbopen(PATH_SPAMD_DB, O_EXLOCK|O_RDWR, 0600, DB_BTREE, 1094 &btreeinfo); 1095 if (db1 == NULL) { 1096 syslog_r(LOG_ERR, &sdata, 1097 "corrupt db in %s, remove and restart", PATH_SPAMD_DB); 1098 exit(1); 1099 } 1100 1101 if ((fd = mkstemp(sfn)) == -1) { 1102 syslog_r(LOG_ERR, &sdata, 1103 "can't convert %s: mkstemp failed (%m)", PATH_SPAMD_DB); 1104 exit(1); 1105 } 1106 memset(&hashinfo, 0, sizeof(hashinfo)); 1107 db2 = dbopen(sfn, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 1108 if (db2 == NULL) { 1109 unlink(sfn); 1110 syslog_r(LOG_ERR, &sdata, 1111 "can't convert %s: can't dbopen %s (%m)", PATH_SPAMD_DB, 1112 sfn); 1113 db1->close(db1); 1114 exit(1); 1115 } 1116 1117 memset(&dbk, 0, sizeof(dbk)); 1118 memset(&dbd, 0, sizeof(dbd)); 1119 for (r = db1->seq(db1, &dbk, &dbd, R_FIRST); !r; 1120 r = db1->seq(db1, &dbk, &dbd, R_NEXT)) { 1121 if (db2->put(db2, &dbk, &dbd, 0)) { 1122 db2->sync(db2, 0); 1123 db2->close(db2); 1124 db1->close(db1); 1125 unlink(sfn); 1126 syslog_r(LOG_ERR, &sdata, 1127 "can't convert %s - remove and restart", 1128 PATH_SPAMD_DB); 1129 exit(1); 1130 } 1131 } 1132 db2->sync(db2, 0); 1133 db2->close(db2); 1134 db1->sync(db1, 0); 1135 db1->close(db1); 1136 rename(sfn, PATH_SPAMD_DB); 1137 close(fd); 1138 /* if we are dropping privs, chown to that user */ 1139 if (pw && (chown(PATH_SPAMD_DB, pw->pw_uid, pw->pw_gid) == -1)) { 1140 syslog_r(LOG_ERR, &sdata, 1141 "chown %s failed (%m)", PATH_SPAMD_DB); 1142 exit(1); 1143 } 1144 } 1145 1146 static void 1147 check_spamd_db(void) 1148 { 1149 HASHINFO hashinfo; 1150 int i = -1; 1151 DB *db; 1152 1153 /* check to see if /var/db/spamd exists, if not, create it */ 1154 memset(&hashinfo, 0, sizeof(hashinfo)); 1155 db = dbopen(PATH_SPAMD_DB, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo); 1156 1157 if (db == NULL) { 1158 switch (errno) { 1159 case ENOENT: 1160 i = open(PATH_SPAMD_DB, O_RDWR|O_CREAT, 0644); 1161 if (i == -1) { 1162 syslog_r(LOG_ERR, &sdata, 1163 "create %s failed (%m)", PATH_SPAMD_DB); 1164 exit(1); 1165 } 1166 /* if we are dropping privs, chown to that user */ 1167 if (pw && (fchown(i, pw->pw_uid, pw->pw_gid) == -1)) { 1168 syslog_r(LOG_ERR, &sdata, 1169 "chown %s failed (%m)", PATH_SPAMD_DB); 1170 exit(1); 1171 } 1172 close(i); 1173 drop_privs(); 1174 return; 1175 break; 1176 case EFTYPE: 1177 /* 1178 * db may be old BTREE instead of HASH, attempt to 1179 * convert. 1180 */ 1181 convert_spamd_db(); 1182 drop_privs(); 1183 return; 1184 break; 1185 default: 1186 syslog_r(LOG_ERR, &sdata, "open of %s failed (%m)", 1187 PATH_SPAMD_DB); 1188 exit(1); 1189 } 1190 } 1191 db->sync(db, 0); 1192 db->close(db); 1193 drop_privs(); 1194 } 1195 1196 1197 int 1198 greywatcher(void) 1199 { 1200 struct sigaction sa; 1201 1202 check_spamd_db(); 1203 1204 startup = time(NULL); 1205 db_pid = fork(); 1206 switch (db_pid) { 1207 case -1: 1208 syslog_r(LOG_ERR, &sdata, "fork failed (%m)"); 1209 exit(1); 1210 case 0: 1211 /* 1212 * child, talks to jailed spamd over greypipe, 1213 * updates db. has no access to pf. 1214 */ 1215 close(pfdev); 1216 setproctitle("(%s update)", PATH_SPAMD_DB); 1217 greyreader(); 1218 syslog_r(LOG_ERR, &sdata, "greyreader failed (%m)"); 1219 /* NOTREACHED */ 1220 _exit(1); 1221 } 1222 1223 1224 fclose(grey); 1225 /* 1226 * parent, scans db periodically for changes and updates 1227 * pf whitelist table accordingly. 1228 */ 1229 1230 sigfillset(&sa.sa_mask); 1231 sa.sa_flags = SA_RESTART; 1232 sa.sa_handler = sig_term_chld; 1233 sigaction(SIGTERM, &sa, NULL); 1234 sigaction(SIGHUP, &sa, NULL); 1235 sigaction(SIGCHLD, &sa, NULL); 1236 sigaction(SIGINT, &sa, NULL); 1237 1238 setproctitle("(pf <spamd-white> update)"); 1239 greyscanner(); 1240 /* NOTREACHED */ 1241 exit(1); 1242 } 1243