1 /* $OpenBSD: spamd.c,v 1.156 2019/08/06 13:34:36 mestre Exp $ */ 2 3 /* 4 * Copyright (c) 2015 Henning Brauer <henning@openbsd.org> 5 * Copyright (c) 2002-2007 Bob Beck. All rights reserved. 6 * Copyright (c) 2002 Theo de Raadt. All rights reserved. 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/types.h> 22 #include <sys/socket.h> 23 #include <sys/sysctl.h> 24 #include <sys/resource.h> 25 #include <sys/signal.h> 26 #include <sys/stat.h> 27 28 #include <netinet/in.h> 29 #include <arpa/inet.h> 30 31 #include <err.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <limits.h> 35 #include <poll.h> 36 #include <pwd.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <syslog.h> 41 #include <unistd.h> 42 #include <limits.h> 43 #include <tls.h> 44 45 #include <netdb.h> 46 47 #include "sdl.h" 48 #include "grey.h" 49 #include "sync.h" 50 51 struct con { 52 struct pollfd *pfd; 53 int state; 54 int laststate; 55 int af; 56 int il; 57 struct sockaddr_storage ss; 58 void *ia; 59 char addr[32]; 60 char caddr[32]; 61 char helo[MAX_MAIL], mail[MAX_MAIL], rcpt[MAX_MAIL]; 62 struct sdlist **blacklists; 63 struct tls *cctx; 64 65 /* 66 * we will do stuttering by changing these to time_t's of 67 * now + n, and only advancing when the time is in the past/now 68 */ 69 time_t r; 70 time_t w; 71 time_t s; 72 73 char ibuf[8192]; 74 char *ip; 75 char rend[5]; /* any chars in here causes input termination */ 76 77 char *obuf; 78 char *lists; 79 size_t osize; 80 char *op; 81 int ol; 82 int data_lines; 83 int data_body; 84 int stutter; 85 int badcmd; 86 int sr; 87 int tlsaction; 88 } *con; 89 90 #define SPAMD_TLS_ACT_NONE 0 91 #define SPAMD_TLS_ACT_READ_POLLIN 1 92 #define SPAMD_TLS_ACT_READ_POLLOUT 2 93 #define SPAMD_TLS_ACT_WRITE_POLLIN 3 94 #define SPAMD_TLS_ACT_WRITE_POLLOUT 4 95 96 #define SPAMD_USER "_spamd" 97 98 void usage(void); 99 char *grow_obuf(struct con *, int); 100 int parse_configline(char *); 101 void parse_configs(void); 102 void do_config(void); 103 int append_error_string (struct con *, size_t, char *, int, void *); 104 void doreply(struct con *); 105 void setlog(char *, size_t, char *); 106 void initcon(struct con *, int, struct sockaddr *); 107 void closecon(struct con *); 108 int match(const char *, const char *); 109 void nextstate(struct con *); 110 void handler(struct con *); 111 void handlew(struct con *, int one); 112 char *loglists(struct con *); 113 void getcaddr(struct con *); 114 void gethelo(char *, size_t, char *); 115 int read_configline(FILE *); 116 void spamd_tls_init(void); 117 void check_spamd_db(void); 118 void blackcheck(int); 119 120 char hostname[HOST_NAME_MAX+1]; 121 struct syslog_data sdata = SYSLOG_DATA_INIT; 122 char *nreply = "450"; 123 char *spamd = "spamd IP-based SPAM blocker"; 124 int greyback[2]; 125 int greypipe[2]; 126 int trappipe[2]; 127 FILE *grey; 128 FILE *trapcfg; 129 time_t passtime = PASSTIME; 130 time_t greyexp = GREYEXP; 131 time_t whiteexp = WHITEEXP; 132 time_t trapexp = TRAPEXP; 133 struct passwd *pw; 134 pid_t jail_pid = -1; 135 u_short cfg_port; 136 u_short sync_port; 137 struct tls_config *tlscfg; 138 struct tls *tlsctx; 139 uint8_t *pubcert; 140 size_t pubcertlen; 141 uint8_t *privkey; 142 size_t privkeylen; 143 char *tlskeyfile = NULL; 144 char *tlscertfile = NULL; 145 146 extern struct sdlist *blacklists; 147 extern int pfdev; 148 extern char *low_prio_mx_ip; 149 150 time_t slowdowntill; 151 152 int conffd = -1; 153 int trapfd = -1; 154 char *cb; 155 size_t cbs, cbu; 156 157 time_t t; 158 159 #define MAXCON 800 160 int maxfiles; 161 int maxcon = MAXCON; 162 int maxblack = MAXCON; 163 int blackcount; 164 int clients; 165 int debug; 166 int greylist = 1; 167 int grey_stutter = 10; 168 int verbose; 169 int stutter = 1; 170 int window; 171 int syncrecv; 172 int syncsend; 173 #define MAXTIME 400 174 175 #define MAXIMUM(a,b) (((a)>(b))?(a):(b)) 176 177 void 178 usage(void) 179 { 180 extern char *__progname; 181 182 fprintf(stderr, 183 "usage: %s [-45bdv] [-B maxblack] [-C file] [-c maxcon] " 184 "[-G passtime:greyexp:whiteexp]\n" 185 "\t[-h hostname] [-K file] [-l address] [-M address] [-n name]\n" 186 "\t[-p port] [-S secs] [-s secs] " 187 "[-w window] [-Y synctarget] [-y synclisten]\n", 188 __progname); 189 190 exit(1); 191 } 192 193 char * 194 grow_obuf(struct con *cp, int off) 195 { 196 char *tmp; 197 198 tmp = realloc(cp->obuf, cp->osize + 8192); 199 if (tmp == NULL) { 200 free(cp->obuf); 201 cp->obuf = NULL; 202 cp->osize = 0; 203 return (NULL); 204 } else { 205 cp->osize += 8192; 206 cp->obuf = tmp; 207 return (cp->obuf + off); 208 } 209 } 210 211 int 212 parse_configline(char *line) 213 { 214 char *cp, prev, *name, *msg, *tmp; 215 char **v4 = NULL, **v6 = NULL; 216 const char *errstr; 217 u_int nv4 = 0, nv6 = 0; 218 int mdone = 0; 219 sa_family_t af; 220 221 name = line; 222 223 for (cp = name; *cp && *cp != ';'; cp++) 224 ; 225 if (*cp != ';') 226 goto parse_error; 227 *cp++ = '\0'; 228 if (!*cp) { 229 sdl_del(name); 230 return (0); 231 } 232 msg = cp; 233 if (*cp++ != '"') 234 goto parse_error; 235 prev = '\0'; 236 for (; !mdone; cp++) { 237 switch (*cp) { 238 case '\\': 239 if (!prev) 240 prev = *cp; 241 else 242 prev = '\0'; 243 break; 244 case '"': 245 if (prev != '\\') { 246 cp++; 247 if (*cp == ';') { 248 mdone = 1; 249 *cp = '\0'; 250 } else { 251 if (debug > 0) 252 printf("bad message: %s\n", msg); 253 goto parse_error; 254 } 255 } 256 break; 257 case '\0': 258 if (debug > 0) 259 printf("bad message: %s\n", msg); 260 goto parse_error; 261 default: 262 prev = '\0'; 263 break; 264 } 265 } 266 267 while ((tmp = strsep(&cp, ";")) != NULL) { 268 char **av; 269 u_int au, ac; 270 271 if (*tmp == '\0') 272 continue; 273 274 if (strncmp(tmp, "inet", 4) != 0) 275 goto parse_error; 276 switch (tmp[4]) { 277 case '\0': 278 af = AF_INET; 279 break; 280 case '6': 281 if (tmp[5] == '\0') { 282 af = AF_INET6; 283 break; 284 } 285 /* FALLTHROUGH */ 286 default: 287 if (debug > 0) 288 printf("unsupported address family: %s\n", tmp); 289 goto parse_error; 290 } 291 292 tmp = strsep(&cp, ";"); 293 if (tmp == NULL) { 294 if (debug > 0) 295 printf("missing address count\n"); 296 goto parse_error; 297 } 298 ac = strtonum(tmp, 0, UINT_MAX, &errstr); 299 if (errstr != NULL) { 300 if (debug > 0) 301 printf("count \"%s\" is %s\n", tmp, errstr); 302 goto parse_error; 303 } 304 305 av = reallocarray(NULL, ac, sizeof(char *)); 306 for (au = 0; au < ac; au++) { 307 tmp = strsep(&cp, ";"); 308 if (tmp == NULL) { 309 if (debug > 0) 310 printf("expected %u addrs, got %u\n", 311 ac, au + 1); 312 free(av); 313 goto parse_error; 314 } 315 if (*tmp == '\0') 316 continue; 317 av[au] = tmp; 318 } 319 if (af == AF_INET) { 320 if (v4 != NULL) { 321 if (debug > 0) 322 printf("duplicate inet\n"); 323 goto parse_error; 324 } 325 v4 = av; 326 nv4 = ac; 327 } else { 328 if (v6 != NULL) { 329 if (debug > 0) 330 printf("duplicate inet6\n"); 331 goto parse_error; 332 } 333 v6 = av; 334 nv6 = ac; 335 } 336 } 337 if (nv4 == 0 && nv6 == 0) { 338 if (debug > 0) 339 printf("no addresses\n"); 340 goto parse_error; 341 } 342 sdl_add(name, msg, v4, nv4, v6, nv6); 343 free(v4); 344 free(v6); 345 return (0); 346 347 parse_error: 348 if (debug > 0) 349 printf("bogus config line - need 'tag;message;af;count;a/m;a/m;a/m...'\n"); 350 free(v4); 351 free(v6); 352 return (-1); 353 } 354 355 void 356 parse_configs(void) 357 { 358 char *start, *end; 359 size_t i; 360 361 /* We always leave an extra byte for the NUL. */ 362 cb[cbu++] = '\0'; 363 364 start = cb; 365 end = start; 366 for (i = 0; i < cbu; i++) { 367 if (*end == '\n') { 368 *end = '\0'; 369 if (end > start + 1) 370 parse_configline(start); 371 start = ++end; 372 } else 373 ++end; 374 } 375 if (end > start + 1) 376 parse_configline(start); 377 } 378 379 void 380 do_config(void) 381 { 382 int n; 383 384 if (debug > 0) 385 printf("got configuration connection\n"); 386 387 /* Leave an extra byte for the terminating NUL. */ 388 if (cbu + 1 >= cbs) { 389 char *tmp; 390 391 tmp = realloc(cb, cbs + (1024 * 1024)); 392 if (tmp == NULL) { 393 if (debug > 0) 394 warn("realloc"); 395 goto configdone; 396 } 397 cbs += 1024 * 1024; 398 cb = tmp; 399 } 400 401 n = read(conffd, cb + cbu, cbs - cbu); 402 if (debug > 0) 403 printf("read %d config bytes\n", n); 404 if (n == 0) { 405 if (cbu != 0) 406 parse_configs(); 407 goto configdone; 408 } else if (n == -1) { 409 if (debug > 0) 410 warn("read"); 411 goto configdone; 412 } else 413 cbu += n; 414 return; 415 416 configdone: 417 free(cb); 418 cb = NULL; 419 cbs = 0; 420 cbu = 0; 421 close(conffd); 422 conffd = -1; 423 slowdowntill = 0; 424 } 425 426 int 427 read_configline(FILE *config) 428 { 429 char *buf; 430 size_t len; 431 432 if ((buf = fgetln(config, &len))) { 433 if (buf[len - 1] == '\n') 434 buf[len - 1] = '\0'; 435 else 436 return (-1); /* all valid lines end in \n */ 437 parse_configline(buf); 438 } else { 439 syslog_r(LOG_DEBUG, &sdata, "read_configline: fgetln (%m)"); 440 return (-1); 441 } 442 return (0); 443 } 444 445 void 446 spamd_tls_init() 447 { 448 if (tlskeyfile == NULL && tlscertfile == NULL) 449 return; 450 if (tlskeyfile == NULL || tlscertfile == NULL) 451 errx(1, "need key and certificate for TLS"); 452 453 if (tls_init() != 0) 454 errx(1, "failed to initialise tls"); 455 if ((tlscfg = tls_config_new()) == NULL) 456 errx(1, "failed to get tls config"); 457 if ((tlsctx = tls_server()) == NULL) 458 errx(1, "failed to get tls server"); 459 460 if (tls_config_set_protocols(tlscfg, TLS_PROTOCOLS_ALL) != 0) 461 errx(1, "failed to set tls protocols"); 462 463 /* might need user-specified ciphers, tls_config_set_ciphers */ 464 if (tls_config_set_ciphers(tlscfg, "all") != 0) 465 errx(1, "failed to set tls ciphers"); 466 467 if (tls_config_set_cert_mem(tlscfg, pubcert, pubcertlen) == -1) 468 errx(1, "unable to set TLS certificate file %s", tlscertfile); 469 if (tls_config_set_key_mem(tlscfg, privkey, privkeylen) == -1) 470 errx(1, "unable to set TLS key file %s", tlskeyfile); 471 if (tls_configure(tlsctx, tlscfg) != 0) 472 errx(1, "failed to configure TLS - %s", tls_error(tlsctx)); 473 474 /* set hostname to cert's CN unless explicitly given? */ 475 } 476 477 int 478 append_error_string(struct con *cp, size_t off, char *fmt, int af, void *ia) 479 { 480 char sav = '\0'; 481 static int lastcont = 0; 482 char *c = cp->obuf + off; 483 char *s = fmt; 484 size_t len = cp->osize - off; 485 int i = 0; 486 487 if (off == 0) 488 lastcont = 0; 489 490 if (lastcont != 0) 491 cp->obuf[lastcont] = '-'; 492 snprintf(c, len, "%s ", nreply); 493 i += strlen(c); 494 lastcont = off + i - 1; 495 if (*s == '"') 496 s++; 497 while (*s) { 498 /* 499 * Make sure we at minimum, have room to add a 500 * format code (4 bytes), and a v6 address(39 bytes) 501 * and a byte saved in sav. 502 */ 503 if (i >= len - 46) { 504 c = grow_obuf(cp, off); 505 if (c == NULL) 506 return (-1); 507 len = cp->osize - (off + i); 508 } 509 510 if (c[i-1] == '\n') { 511 if (lastcont != 0) 512 cp->obuf[lastcont] = '-'; 513 snprintf(c + i, len, "%s ", nreply); 514 i += strlen(c); 515 lastcont = off + i - 1; 516 } 517 518 switch (*s) { 519 case '\\': 520 case '%': 521 if (!sav) 522 sav = *s; 523 else { 524 c[i++] = sav; 525 sav = '\0'; 526 c[i] = '\0'; 527 } 528 break; 529 case '"': 530 case 'A': 531 case 'n': 532 if (*(s+1) == '\0') { 533 break; 534 } 535 if (sav == '\\' && *s == 'n') { 536 c[i++] = '\n'; 537 sav = '\0'; 538 c[i] = '\0'; 539 break; 540 } else if (sav == '\\' && *s == '"') { 541 c[i++] = '"'; 542 sav = '\0'; 543 c[i] = '\0'; 544 break; 545 } else if (sav == '%' && *s == 'A') { 546 inet_ntop(af, ia, c + i, (len - i)); 547 i += strlen(c + i); 548 sav = '\0'; 549 break; 550 } 551 /* FALLTHROUGH */ 552 default: 553 if (sav) 554 c[i++] = sav; 555 c[i++] = *s; 556 sav = '\0'; 557 c[i] = '\0'; 558 break; 559 } 560 s++; 561 } 562 return (i); 563 } 564 565 char * 566 loglists(struct con *cp) 567 { 568 static char matchlists[80]; 569 struct sdlist **matches; 570 int s = sizeof(matchlists) - 4; 571 572 matchlists[0] = '\0'; 573 matches = cp->blacklists; 574 if (matches == NULL) 575 return (NULL); 576 for (; *matches; matches++) { 577 578 /* don't report an insane amount of lists in the logs. 579 * just truncate and indicate with ... 580 */ 581 if (strlen(matchlists) + strlen(matches[0]->tag) + 1 >= s) 582 strlcat(matchlists, " ...", sizeof(matchlists)); 583 else { 584 strlcat(matchlists, " ", s); 585 strlcat(matchlists, matches[0]->tag, s); 586 } 587 } 588 return matchlists; 589 } 590 591 void 592 doreply(struct con *cp) 593 { 594 struct sdlist **matches; 595 int off = 0; 596 597 matches = cp->blacklists; 598 if (matches == NULL) 599 goto nomatch; 600 for (; *matches; matches++) { 601 int used = 0; 602 int left = cp->osize - off; 603 604 used = append_error_string(cp, off, matches[0]->string, 605 cp->af, cp->ia); 606 if (used == -1) 607 goto bad; 608 off += used; 609 left -= used; 610 if (cp->obuf[off - 1] != '\n') { 611 if (left < 1) { 612 if (grow_obuf(cp, off) == NULL) 613 goto bad; 614 } 615 cp->obuf[off++] = '\n'; 616 cp->obuf[off] = '\0'; 617 } 618 } 619 return; 620 nomatch: 621 /* No match. give generic reply */ 622 free(cp->obuf); 623 if (cp->blacklists != NULL) 624 cp->osize = asprintf(&cp->obuf, 625 "%s-Sorry %s\n" 626 "%s-You are trying to send mail from an address " 627 "listed by one\n" 628 "%s or more IP-based registries as being a SPAM source.\n", 629 nreply, cp->addr, nreply, nreply); 630 else 631 cp->osize = asprintf(&cp->obuf, 632 "451 Temporary failure, please try again later.\r\n"); 633 if (cp->osize == -1) 634 cp->obuf = NULL; 635 cp->osize++; /* size includes the NUL (also changes -1 to 0) */ 636 return; 637 bad: 638 if (cp->obuf != NULL) { 639 free(cp->obuf); 640 cp->obuf = NULL; 641 cp->osize = 0; 642 } 643 } 644 645 void 646 setlog(char *p, size_t len, char *f) 647 { 648 char *s; 649 650 s = strsep(&f, ":"); 651 if (!f) 652 return; 653 while (*f == ' ' || *f == '\t') 654 f++; 655 s = strsep(&f, " \t"); 656 if (s == NULL) 657 return; 658 strlcpy(p, s, len); 659 s = strsep(&p, " \t\n\r"); 660 if (s == NULL) 661 return; 662 s = strsep(&p, " \t\n\r"); 663 if (s) 664 *s = '\0'; 665 } 666 667 /* 668 * Get address client connected to, by doing a getsockname call. 669 * Must not be used with a NAT'ed connection (use divert-to instead of rdr-to). 670 */ 671 void 672 getcaddr(struct con *cp) 673 { 674 struct sockaddr_storage original_destination; 675 struct sockaddr *odp = (struct sockaddr *) &original_destination; 676 socklen_t len = sizeof(struct sockaddr_storage); 677 int error; 678 679 cp->caddr[0] = '\0'; 680 if (getsockname(cp->pfd->fd, odp, &len) == -1) 681 return; 682 error = getnameinfo(odp, odp->sa_len, cp->caddr, sizeof(cp->caddr), 683 NULL, 0, NI_NUMERICHOST); 684 if (error) 685 cp->caddr[0] = '\0'; 686 } 687 688 void 689 gethelo(char *p, size_t len, char *f) 690 { 691 char *s; 692 693 /* skip HELO/EHLO */ 694 f+=4; 695 /* skip whitespace */ 696 while (*f == ' ' || *f == '\t') 697 f++; 698 s = strsep(&f, " \t"); 699 if (s == NULL) 700 return; 701 strlcpy(p, s, len); 702 s = strsep(&p, " \t\n\r"); 703 if (s == NULL) 704 return; 705 s = strsep(&p, " \t\n\r"); 706 if (s) 707 *s = '\0'; 708 } 709 710 void 711 initcon(struct con *cp, int fd, struct sockaddr *sa) 712 { 713 struct pollfd *pfd = cp->pfd; 714 char ctimebuf[26]; 715 time_t tt; 716 int error; 717 718 if (sa->sa_family != AF_INET) 719 errx(1, "not supported yet"); 720 721 time(&tt); 722 free(cp->obuf); 723 free(cp->blacklists); 724 free(cp->lists); 725 memset(cp, 0, sizeof(*cp)); 726 if (grow_obuf(cp, 0) == NULL) 727 err(1, "malloc"); 728 cp->pfd = pfd; 729 cp->pfd->fd = fd; 730 memcpy(&cp->ss, sa, sa->sa_len); 731 cp->af = sa->sa_family; 732 cp->ia = &((struct sockaddr_in *)&cp->ss)->sin_addr; 733 cp->blacklists = sdl_lookup(blacklists, cp->af, cp->ia); 734 cp->stutter = (greylist && !grey_stutter && cp->blacklists == NULL) ? 735 0 : stutter; 736 error = getnameinfo(sa, sa->sa_len, cp->addr, sizeof(cp->addr), NULL, 0, 737 NI_NUMERICHOST); 738 #ifdef useless 739 if (error) 740 errx(1, "%s", gai_strerror(error)); 741 #endif 742 ctime_r(&t, ctimebuf); 743 ctimebuf[sizeof(ctimebuf) - 2] = '\0'; /* nuke newline */ 744 snprintf(cp->obuf, cp->osize, "220 %s ESMTP %s; %s\r\n", 745 hostname, spamd, ctimebuf); 746 cp->op = cp->obuf; 747 cp->ol = strlen(cp->op); 748 cp->w = tt + cp->stutter; 749 cp->s = tt; 750 strlcpy(cp->rend, "\n", sizeof cp->rend); 751 clients++; 752 if (cp->blacklists != NULL) { 753 blackcount++; 754 if (greylist && blackcount > maxblack) 755 cp->stutter = 0; 756 cp->lists = strdup(loglists(cp)); 757 if (cp->lists == NULL) 758 err(1, "malloc"); 759 } 760 else 761 cp->lists = NULL; 762 } 763 764 void 765 closecon(struct con *cp) 766 { 767 time_t tt; 768 769 if (cp->cctx) { 770 tls_close(cp->cctx); 771 tls_free(cp->cctx); 772 } 773 close(cp->pfd->fd); 774 cp->pfd->fd = -1; 775 776 slowdowntill = 0; 777 778 time(&tt); 779 syslog_r(LOG_INFO, &sdata, "%s: disconnected after %lld seconds.%s%s", 780 cp->addr, (long long)(tt - cp->s), 781 ((cp->lists == NULL) ? "" : " lists:"), 782 ((cp->lists == NULL) ? "": cp->lists)); 783 if (debug > 0) 784 printf("%s connected for %lld seconds.\n", cp->addr, 785 (long long)(tt - cp->s)); 786 free(cp->lists); 787 cp->lists = NULL; 788 if (cp->blacklists != NULL) { 789 blackcount--; 790 free(cp->blacklists); 791 cp->blacklists = NULL; 792 } 793 if (cp->obuf != NULL) { 794 free(cp->obuf); 795 cp->obuf = NULL; 796 cp->osize = 0; 797 } 798 clients--; 799 } 800 801 int 802 match(const char *s1, const char *s2) 803 { 804 return (strncasecmp(s1, s2, strlen(s2)) == 0); 805 } 806 807 void 808 nextstate(struct con *cp) 809 { 810 if (match(cp->ibuf, "QUIT") && cp->state < 99) { 811 snprintf(cp->obuf, cp->osize, "221 %s\r\n", hostname); 812 cp->op = cp->obuf; 813 cp->ol = strlen(cp->op); 814 cp->w = t + cp->stutter; 815 cp->laststate = cp->state; 816 cp->state = 99; 817 return; 818 } 819 820 if (match(cp->ibuf, "RSET") && cp->state > 2 && cp->state < 50) { 821 snprintf(cp->obuf, cp->osize, 822 "250 Ok to start over.\r\n"); 823 cp->op = cp->obuf; 824 cp->ol = strlen(cp->op); 825 cp->w = t + cp->stutter; 826 cp->laststate = cp->state; 827 cp->state = 2; 828 return; 829 } 830 switch (cp->state) { 831 case 0: 832 tlsinitdone: 833 /* banner sent; wait for input */ 834 cp->ip = cp->ibuf; 835 cp->il = sizeof(cp->ibuf) - 1; 836 cp->laststate = cp->state; 837 cp->state = 1; 838 cp->r = t; 839 break; 840 case 1: 841 /* received input: parse, and select next state */ 842 if (match(cp->ibuf, "HELO") || 843 match(cp->ibuf, "EHLO")) { 844 int nextstate = 2; 845 cp->helo[0] = '\0'; 846 gethelo(cp->helo, sizeof cp->helo, cp->ibuf); 847 if (cp->helo[0] == '\0') { 848 nextstate = 0; 849 snprintf(cp->obuf, cp->osize, 850 "501 helo requires domain name.\r\n"); 851 } else { 852 if (cp->cctx == NULL && tlsctx != NULL && 853 cp->blacklists == NULL && 854 match(cp->ibuf, "EHLO")) { 855 snprintf(cp->obuf, cp->osize, 856 "250-%s\r\n" 857 "250 STARTTLS\r\n", 858 hostname); 859 nextstate = 7; 860 } else { 861 snprintf(cp->obuf, cp->osize, 862 "250 Hello, spam sender. Pleased " 863 "to be wasting your time.\r\n"); 864 } 865 } 866 cp->op = cp->obuf; 867 cp->ol = strlen(cp->op); 868 cp->laststate = cp->state; 869 cp->state = nextstate; 870 cp->w = t + cp->stutter; 871 break; 872 } 873 goto mail; 874 case 2: 875 /* sent 250 Hello, wait for input */ 876 cp->ip = cp->ibuf; 877 cp->il = sizeof(cp->ibuf) - 1; 878 cp->laststate = cp->state; 879 cp->state = 3; 880 cp->r = t; 881 break; 882 case 3: 883 mail: 884 if (match(cp->ibuf, "MAIL")) { 885 setlog(cp->mail, sizeof cp->mail, cp->ibuf); 886 snprintf(cp->obuf, cp->osize, 887 "250 You are about to try to deliver spam. " 888 "Your time will be spent, for nothing.\r\n"); 889 cp->op = cp->obuf; 890 cp->ol = strlen(cp->op); 891 cp->laststate = cp->state; 892 cp->state = 4; 893 cp->w = t + cp->stutter; 894 break; 895 } 896 goto rcpt; 897 case 4: 898 /* sent 250 Sender ok */ 899 cp->ip = cp->ibuf; 900 cp->il = sizeof(cp->ibuf) - 1; 901 cp->laststate = cp->state; 902 cp->state = 5; 903 cp->r = t; 904 break; 905 case 5: 906 rcpt: 907 if (match(cp->ibuf, "RCPT")) { 908 setlog(cp->rcpt, sizeof(cp->rcpt), cp->ibuf); 909 snprintf(cp->obuf, cp->osize, 910 "250 This is hurting you more than it is " 911 "hurting me.\r\n"); 912 cp->op = cp->obuf; 913 cp->ol = strlen(cp->op); 914 cp->laststate = cp->state; 915 cp->state = 6; 916 cp->w = t + cp->stutter; 917 918 if (cp->mail[0] && cp->rcpt[0]) { 919 if (verbose) 920 syslog_r(LOG_INFO, &sdata, 921 "(%s) %s: %s -> %s", 922 cp->blacklists ? "BLACK" : "GREY", 923 cp->addr, cp->mail, 924 cp->rcpt); 925 if (debug) 926 fprintf(stderr, "(%s) %s: %s -> %s\n", 927 cp->blacklists ? "BLACK" : "GREY", 928 cp->addr, cp->mail, cp->rcpt); 929 if (greylist && cp->blacklists == NULL) { 930 /* send this info to the greylister */ 931 getcaddr(cp); 932 fprintf(grey, 933 "CO:%s\nHE:%s\nIP:%s\nFR:%s\nTO:%s\n", 934 cp->caddr, cp->helo, cp->addr, 935 cp->mail, cp->rcpt); 936 fflush(grey); 937 } 938 } 939 break; 940 } 941 goto spam; 942 case 6: 943 /* sent 250 blah */ 944 cp->ip = cp->ibuf; 945 cp->il = sizeof(cp->ibuf) - 1; 946 cp->laststate = cp->state; 947 cp->state = 5; 948 cp->r = t; 949 break; 950 case 7: 951 /* sent 250 STARTTLS, wait for input */ 952 cp->ip = cp->ibuf; 953 cp->il = sizeof(cp->ibuf) - 1; 954 cp->laststate = cp->state; 955 cp->state = 8; 956 cp->r = t; 957 break; 958 case 8: 959 if (tlsctx != NULL && cp->blacklists == NULL && 960 cp->cctx == NULL && match(cp->ibuf, "STARTTLS")) { 961 snprintf(cp->obuf, cp->osize, 962 "220 glad you want to burn more CPU cycles on " 963 "your spam\r\n"); 964 cp->op = cp->obuf; 965 cp->ol = strlen(cp->op); 966 cp->laststate = cp->state; 967 cp->state = 9; 968 cp->w = t + cp->stutter; 969 break; 970 } 971 goto mail; 972 case 9: 973 if (tls_accept_socket(tlsctx, &cp->cctx, cp->pfd->fd) == -1) { 974 snprintf(cp->obuf, cp->osize, 975 "500 STARTTLS failed\r\n"); 976 cp->op = cp->obuf; 977 cp->ol = strlen(cp->op); 978 cp->laststate = cp->state; 979 cp->state = 98; 980 goto done; 981 } 982 goto tlsinitdone; 983 984 case 50: 985 spam: 986 if (match(cp->ibuf, "DATA")) { 987 snprintf(cp->obuf, cp->osize, 988 "354 Enter spam, end with \".\" on a line by " 989 "itself\r\n"); 990 cp->state = 60; 991 if (window && setsockopt(cp->pfd->fd, SOL_SOCKET, 992 SO_RCVBUF, &window, sizeof(window)) == -1) { 993 syslog_r(LOG_DEBUG, &sdata,"setsockopt: %m"); 994 /* don't fail if this doesn't work. */ 995 } 996 cp->ip = cp->ibuf; 997 cp->il = sizeof(cp->ibuf) - 1; 998 cp->op = cp->obuf; 999 cp->ol = strlen(cp->op); 1000 cp->w = t + cp->stutter; 1001 if (greylist && cp->blacklists == NULL) { 1002 cp->laststate = cp->state; 1003 cp->state = 98; 1004 goto done; 1005 } 1006 } else { 1007 if (match(cp->ibuf, "NOOP")) 1008 snprintf(cp->obuf, cp->osize, 1009 "250 2.0.0 OK I did nothing\r\n"); 1010 else { 1011 snprintf(cp->obuf, cp->osize, 1012 "500 5.5.1 Command unrecognized\r\n"); 1013 cp->badcmd++; 1014 if (cp->badcmd > 20) { 1015 cp->laststate = cp->state; 1016 cp->state = 98; 1017 goto done; 1018 } 1019 } 1020 cp->state = cp->laststate; 1021 cp->ip = cp->ibuf; 1022 cp->il = sizeof(cp->ibuf) - 1; 1023 cp->op = cp->obuf; 1024 cp->ol = strlen(cp->op); 1025 cp->w = t + cp->stutter; 1026 } 1027 break; 1028 case 60: 1029 /* sent 354 blah */ 1030 cp->ip = cp->ibuf; 1031 cp->il = sizeof(cp->ibuf) - 1; 1032 cp->laststate = cp->state; 1033 cp->state = 70; 1034 cp->r = t; 1035 break; 1036 case 70: { 1037 char *p, *q; 1038 1039 for (p = q = cp->ibuf; q <= cp->ip; ++q) 1040 if (*q == '\n' || q == cp->ip) { 1041 *q = 0; 1042 if (q > p && q[-1] == '\r') 1043 q[-1] = 0; 1044 if (!strcmp(p, ".") || 1045 (cp->data_body && ++cp->data_lines >= 10)) { 1046 cp->laststate = cp->state; 1047 cp->state = 98; 1048 goto done; 1049 } 1050 if (!cp->data_body && !*p) 1051 cp->data_body = 1; 1052 if (verbose && cp->data_body && *p) 1053 syslog_r(LOG_DEBUG, &sdata, "%s: " 1054 "Body: %s", cp->addr, p); 1055 else if (verbose && (match(p, "FROM:") || 1056 match(p, "TO:") || match(p, "SUBJECT:"))) 1057 syslog_r(LOG_INFO, &sdata, "%s: %s", 1058 cp->addr, p); 1059 p = ++q; 1060 } 1061 cp->ip = cp->ibuf; 1062 cp->il = sizeof(cp->ibuf) - 1; 1063 cp->r = t; 1064 break; 1065 } 1066 case 98: 1067 done: 1068 doreply(cp); 1069 cp->op = cp->obuf; 1070 cp->ol = strlen(cp->op); 1071 cp->w = t + cp->stutter; 1072 cp->laststate = cp->state; 1073 cp->state = 99; 1074 break; 1075 case 99: 1076 closecon(cp); 1077 break; 1078 default: 1079 errx(1, "illegal state %d", cp->state); 1080 break; 1081 } 1082 } 1083 1084 void 1085 handler(struct con *cp) 1086 { 1087 int end = 0; 1088 ssize_t n; 1089 1090 if (cp->r || cp->tlsaction != SPAMD_TLS_ACT_NONE) { 1091 if (cp->cctx) { 1092 cp->tlsaction = SPAMD_TLS_ACT_NONE; 1093 n = tls_read(cp->cctx, cp->ip, cp->il); 1094 if (n == TLS_WANT_POLLIN) 1095 cp->tlsaction = SPAMD_TLS_ACT_READ_POLLIN; 1096 if (n == TLS_WANT_POLLOUT) 1097 cp->tlsaction = SPAMD_TLS_ACT_READ_POLLOUT; 1098 if (cp->tlsaction != SPAMD_TLS_ACT_NONE) 1099 return; 1100 } else 1101 n = read(cp->pfd->fd, cp->ip, cp->il); 1102 1103 if (n == 0) 1104 closecon(cp); 1105 else if (n == -1) { 1106 if (errno == EAGAIN) 1107 return; 1108 if (debug > 0) 1109 warn("read"); 1110 closecon(cp); 1111 } else { 1112 cp->ip[n] = '\0'; 1113 if (cp->rend[0]) 1114 if (strpbrk(cp->ip, cp->rend)) 1115 end = 1; 1116 cp->ip += n; 1117 cp->il -= n; 1118 } 1119 } 1120 if (end || cp->il == 0) { 1121 while (cp->ip > cp->ibuf && 1122 (cp->ip[-1] == '\r' || cp->ip[-1] == '\n')) 1123 cp->ip--; 1124 *cp->ip = '\0'; 1125 cp->r = 0; 1126 nextstate(cp); 1127 } 1128 } 1129 1130 void 1131 handlew(struct con *cp, int one) 1132 { 1133 ssize_t n; 1134 1135 /* kill stutter on greylisted connections after initial delay */ 1136 if (cp->stutter && greylist && cp->blacklists == NULL && 1137 (t - cp->s) > grey_stutter) 1138 cp->stutter=0; 1139 1140 if (cp->w || cp->tlsaction != SPAMD_TLS_ACT_NONE) { 1141 if (*cp->op == '\n' && !cp->sr) { 1142 /* insert \r before \n */ 1143 if (cp->cctx) { 1144 cp->tlsaction = SPAMD_TLS_ACT_NONE; 1145 n = tls_write(cp->cctx, "\r", 1); 1146 if (n == TLS_WANT_POLLIN) 1147 cp->tlsaction = 1148 SPAMD_TLS_ACT_WRITE_POLLIN; 1149 if (n == TLS_WANT_POLLOUT) 1150 cp->tlsaction = 1151 SPAMD_TLS_ACT_WRITE_POLLOUT; 1152 if (cp->tlsaction != SPAMD_TLS_ACT_NONE) 1153 return; 1154 } else 1155 n = write(cp->pfd->fd, "\r", 1); 1156 1157 if (n == 0) { 1158 closecon(cp); 1159 goto handled; 1160 } else if (n == -1) { 1161 if (errno == EAGAIN) 1162 return; 1163 if (debug > 0 && errno != EPIPE) 1164 warn("write"); 1165 closecon(cp); 1166 goto handled; 1167 } 1168 } 1169 if (*cp->op == '\r') 1170 cp->sr = 1; 1171 else 1172 cp->sr = 0; 1173 if (cp->cctx) { 1174 cp->tlsaction = SPAMD_TLS_ACT_NONE; 1175 n = tls_write(cp->cctx, cp->op, cp->ol); 1176 if (n == TLS_WANT_POLLIN) 1177 cp->tlsaction = SPAMD_TLS_ACT_WRITE_POLLIN; 1178 if (n == TLS_WANT_POLLOUT) 1179 cp->tlsaction = SPAMD_TLS_ACT_WRITE_POLLOUT; 1180 if (cp->tlsaction != SPAMD_TLS_ACT_NONE) 1181 return; 1182 } else 1183 n = write(cp->pfd->fd, cp->op, 1184 (one && cp->stutter) ? 1 : cp->ol); 1185 1186 if (n == 0) 1187 closecon(cp); 1188 else if (n == -1) { 1189 if (errno == EAGAIN) 1190 return; 1191 if (debug > 0 && errno != EPIPE) 1192 warn("write"); 1193 closecon(cp); 1194 } else { 1195 cp->op += n; 1196 cp->ol -= n; 1197 } 1198 } 1199 handled: 1200 cp->w = t + cp->stutter; 1201 if (cp->ol == 0) { 1202 cp->w = 0; 1203 nextstate(cp); 1204 } 1205 } 1206 1207 static int 1208 get_maxfiles(void) 1209 { 1210 int mib[2], maxfiles; 1211 size_t len; 1212 1213 mib[0] = CTL_KERN; 1214 mib[1] = KERN_MAXFILES; 1215 len = sizeof(maxfiles); 1216 if (sysctl(mib, 2, &maxfiles, &len, NULL, 0) == -1) 1217 return(MAXCON); 1218 if ((maxfiles - 200) < 10) 1219 errx(1, "kern.maxfiles is only %d, can not continue\n", 1220 maxfiles); 1221 else 1222 return(maxfiles - 200); 1223 } 1224 1225 /* Symbolic indexes for pfd[] below */ 1226 #define PFD_SMTPLISTEN 0 1227 #define PFD_CONFLISTEN 1 1228 #define PFD_SYNCFD 2 1229 #define PFD_CONFFD 3 1230 #define PFD_TRAPFD 4 1231 #define PFD_GREYBACK 5 1232 #define PFD_FIRSTCON 6 1233 1234 int 1235 main(int argc, char *argv[]) 1236 { 1237 struct pollfd *pfd; 1238 struct sockaddr_in sin; 1239 struct sockaddr_in lin; 1240 int ch, smtplisten, conflisten, syncfd = -1, i, one = 1; 1241 u_short port; 1242 long long passt, greyt, whitet; 1243 struct servent *ent; 1244 struct rlimit rlp; 1245 char *bind_address = NULL; 1246 const char *errstr; 1247 char *sync_iface = NULL; 1248 char *sync_baddr = NULL; 1249 struct addrinfo hints, *res; 1250 char *addr; 1251 char portstr[6]; 1252 int error; 1253 1254 tzset(); 1255 openlog_r("spamd", LOG_PID | LOG_NDELAY, LOG_DAEMON, &sdata); 1256 1257 if ((ent = getservbyname("spamd", "tcp")) == NULL) 1258 errx(1, "Can't find service \"spamd\" in /etc/services"); 1259 port = ntohs(ent->s_port); 1260 if ((ent = getservbyname("spamd-cfg", "tcp")) == NULL) 1261 errx(1, "Can't find service \"spamd-cfg\" in /etc/services"); 1262 cfg_port = ntohs(ent->s_port); 1263 if ((ent = getservbyname("spamd-sync", "udp")) == NULL) 1264 errx(1, "Can't find service \"spamd-sync\" in /etc/services"); 1265 sync_port = ntohs(ent->s_port); 1266 1267 if (gethostname(hostname, sizeof hostname) == -1) 1268 err(1, "gethostname"); 1269 maxfiles = get_maxfiles(); 1270 if (maxcon > maxfiles) 1271 maxcon = maxfiles; 1272 if (maxblack > maxfiles) 1273 maxblack = maxfiles; 1274 while ((ch = 1275 getopt(argc, argv, "45l:c:B:p:bdG:h:s:S:M:n:vw:y:Y:C:K:")) != -1) { 1276 switch (ch) { 1277 case '4': 1278 nreply = "450"; 1279 break; 1280 case '5': 1281 nreply = "550"; 1282 break; 1283 case 'l': 1284 bind_address = optarg; 1285 break; 1286 case 'B': 1287 maxblack = strtonum(optarg, 0, INT_MAX, &errstr); 1288 if (errstr) 1289 errx(1, "-B %s: %s", optarg, errstr); 1290 break; 1291 case 'c': 1292 maxcon = strtonum(optarg, 1, maxfiles, &errstr); 1293 if (errstr) { 1294 fprintf(stderr, "-c %s: %s\n", optarg, errstr); 1295 usage(); 1296 } 1297 break; 1298 case 'p': 1299 port = strtonum(optarg, 1, USHRT_MAX, &errstr); 1300 if (errstr) 1301 errx(1, "-p %s: %s", optarg, errstr); 1302 break; 1303 case 'd': 1304 debug = 1; 1305 break; 1306 case 'b': 1307 greylist = 0; 1308 break; 1309 case 'G': 1310 if (sscanf(optarg, "%lld:%lld:%lld", &passt, &greyt, 1311 &whitet) != 3) 1312 usage(); 1313 passtime = passt; 1314 greyexp = greyt; 1315 whiteexp = whitet; 1316 /* convert to seconds from minutes */ 1317 passtime *= 60; 1318 /* convert to seconds from hours */ 1319 whiteexp *= (60 * 60); 1320 /* convert to seconds from hours */ 1321 greyexp *= (60 * 60); 1322 break; 1323 case 'h': 1324 memset(hostname, 0, sizeof(hostname)); 1325 if (strlcpy(hostname, optarg, sizeof(hostname)) >= 1326 sizeof(hostname)) 1327 errx(1, "-h arg too long"); 1328 break; 1329 case 's': 1330 stutter = strtonum(optarg, 0, 10, &errstr); 1331 if (errstr) 1332 usage(); 1333 break; 1334 case 'S': 1335 grey_stutter = strtonum(optarg, 0, 90, &errstr); 1336 if (errstr) 1337 usage(); 1338 break; 1339 case 'M': 1340 low_prio_mx_ip = optarg; 1341 break; 1342 case 'n': 1343 spamd = optarg; 1344 break; 1345 case 'v': 1346 verbose = 1; 1347 break; 1348 case 'w': 1349 window = strtonum(optarg, 1, INT_MAX, &errstr); 1350 if (errstr) 1351 errx(1, "-w %s: %s", optarg, errstr); 1352 break; 1353 case 'Y': 1354 if (sync_addhost(optarg, sync_port) != 0) 1355 sync_iface = optarg; 1356 syncsend++; 1357 break; 1358 case 'y': 1359 sync_baddr = optarg; 1360 syncrecv++; 1361 break; 1362 case 'C': 1363 tlscertfile = optarg; 1364 break; 1365 case 'K': 1366 tlskeyfile = optarg; 1367 break; 1368 default: 1369 usage(); 1370 break; 1371 } 1372 } 1373 1374 setproctitle("[priv]%s%s", 1375 greylist ? " (greylist)" : "", 1376 (syncrecv || syncsend) ? " (sync)" : ""); 1377 1378 if (syncsend || syncrecv) { 1379 syncfd = sync_init(sync_iface, sync_baddr, sync_port); 1380 if (syncfd == -1) 1381 err(1, "sync init"); 1382 } 1383 1384 if (geteuid()) 1385 errx(1, "need root privileges"); 1386 1387 if ((pw = getpwnam(SPAMD_USER)) == NULL) 1388 errx(1, "no such user %s", SPAMD_USER); 1389 1390 if (!greylist) { 1391 maxblack = maxcon; 1392 } else if (maxblack > maxcon) 1393 usage(); 1394 1395 if (tlscertfile && 1396 (pubcert=tls_load_file(tlscertfile, &pubcertlen, NULL)) == NULL) 1397 errx(1, "unable to load TLS certificate file %s", tlscertfile); 1398 if (tlskeyfile && 1399 (privkey=tls_load_file(tlskeyfile, &privkeylen, NULL)) == NULL) 1400 errx(1, "unable to load TLS key file %s", tlskeyfile); 1401 1402 rlp.rlim_cur = rlp.rlim_max = maxcon + 15; 1403 if (setrlimit(RLIMIT_NOFILE, &rlp) == -1) 1404 err(1, "setrlimit"); 1405 1406 pfd = reallocarray(NULL, PFD_FIRSTCON + maxcon, sizeof(*pfd)); 1407 if (pfd == NULL) 1408 err(1, "reallocarray"); 1409 1410 con = calloc(maxcon, sizeof(*con)); 1411 if (con == NULL) 1412 err(1, "calloc"); 1413 1414 con->obuf = malloc(8192); 1415 1416 if (con->obuf == NULL) 1417 err(1, "malloc"); 1418 con->osize = 8192; 1419 1420 for (i = 0; i < maxcon; i++) { 1421 con[i].pfd = &pfd[PFD_FIRSTCON + i]; 1422 con[i].pfd->fd = -1; 1423 } 1424 1425 signal(SIGPIPE, SIG_IGN); 1426 1427 smtplisten = socket(AF_INET, SOCK_STREAM, 0); 1428 if (smtplisten == -1) 1429 err(1, "socket"); 1430 1431 if (setsockopt(smtplisten, SOL_SOCKET, SO_REUSEADDR, &one, 1432 sizeof(one)) == -1) 1433 return (-1); 1434 1435 conflisten = socket(AF_INET, SOCK_STREAM, 0); 1436 if (conflisten == -1) 1437 err(1, "socket"); 1438 1439 if (setsockopt(conflisten, SOL_SOCKET, SO_REUSEADDR, &one, 1440 sizeof(one)) == -1) 1441 return (-1); 1442 1443 memset(&hints, 0, sizeof(hints)); 1444 hints.ai_family = AF_INET; 1445 addr = bind_address; 1446 snprintf(portstr, sizeof(portstr), "%hu", port); 1447 1448 if ((error = getaddrinfo(addr, portstr, &hints, &res)) != 0) { 1449 errx(1, "getaddrinfo: %s", gai_strerror(error)); 1450 } 1451 1452 if (bind(smtplisten, res->ai_addr, res->ai_addrlen) == -1) { 1453 freeaddrinfo(res); 1454 err(1, "bind"); 1455 } 1456 freeaddrinfo(res); 1457 1458 memset(&lin, 0, sizeof sin); 1459 lin.sin_len = sizeof(sin); 1460 lin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 1461 lin.sin_family = AF_INET; 1462 lin.sin_port = htons(cfg_port); 1463 1464 if (bind(conflisten, (struct sockaddr *)&lin, sizeof lin) == -1) 1465 err(1, "bind local"); 1466 1467 if (debug == 0) { 1468 if (daemon(1, 1) == -1) 1469 err(1, "daemon"); 1470 } 1471 1472 if (greylist) { 1473 pfdev = open("/dev/pf", O_RDWR); 1474 if (pfdev == -1) { 1475 syslog_r(LOG_ERR, &sdata, "open /dev/pf: %m"); 1476 exit(1); 1477 } 1478 1479 check_spamd_db(); 1480 1481 maxblack = (maxblack >= maxcon) ? maxcon - 100 : maxblack; 1482 if (maxblack < 0) 1483 maxblack = 0; 1484 1485 /* open pipe to talk to greylister */ 1486 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, greyback) == -1) { 1487 syslog(LOG_ERR, "socketpair (%m)"); 1488 exit(1); 1489 } 1490 if (pipe(greypipe) == -1) { 1491 syslog(LOG_ERR, "pipe (%m)"); 1492 exit(1); 1493 } 1494 /* open pipe to receive spamtrap configs */ 1495 if (pipe(trappipe) == -1) { 1496 syslog(LOG_ERR, "pipe (%m)"); 1497 exit(1); 1498 } 1499 jail_pid = fork(); 1500 switch (jail_pid) { 1501 case -1: 1502 syslog(LOG_ERR, "fork (%m)"); 1503 exit(1); 1504 case 0: 1505 /* child - continue */ 1506 signal(SIGPIPE, SIG_IGN); 1507 grey = fdopen(greypipe[1], "w"); 1508 if (grey == NULL) { 1509 syslog(LOG_ERR, "fdopen (%m)"); 1510 _exit(1); 1511 } 1512 close(greyback[0]); 1513 close(greypipe[0]); 1514 trapfd = trappipe[0]; 1515 trapcfg = fdopen(trappipe[0], "r"); 1516 if (trapcfg == NULL) { 1517 syslog(LOG_ERR, "fdopen (%m)"); 1518 _exit(1); 1519 } 1520 close(trappipe[1]); 1521 1522 if (setgroups(1, &pw->pw_gid) || 1523 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 1524 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 1525 err(1, "failed to drop privs"); 1526 1527 goto jail; 1528 } 1529 /* parent - run greylister */ 1530 close(greyback[1]); 1531 grey = fdopen(greypipe[0], "r"); 1532 if (grey == NULL) { 1533 syslog(LOG_ERR, "fdopen (%m)"); 1534 exit(1); 1535 } 1536 close(greypipe[1]); 1537 trapcfg = fdopen(trappipe[1], "w"); 1538 if (trapcfg == NULL) { 1539 syslog(LOG_ERR, "fdopen (%m)"); 1540 exit(1); 1541 } 1542 close(trappipe[0]); 1543 return (greywatcher()); 1544 } 1545 1546 jail: 1547 if (pledge("stdio inet", NULL) == -1) 1548 err(1, "pledge"); 1549 1550 spamd_tls_init(); 1551 1552 if (listen(smtplisten, 10) == -1) 1553 err(1, "listen"); 1554 1555 if (listen(conflisten, 10) == -1) 1556 err(1, "listen"); 1557 1558 if (debug != 0) 1559 printf("listening for incoming connections.\n"); 1560 syslog_r(LOG_WARNING, &sdata, "listening for incoming connections."); 1561 1562 /* We always check for trap and sync events if configured. */ 1563 if (trapfd != -1) { 1564 pfd[PFD_TRAPFD].fd = trapfd; 1565 pfd[PFD_TRAPFD].events = POLLIN; 1566 } else { 1567 pfd[PFD_TRAPFD].fd = -1; 1568 pfd[PFD_TRAPFD].events = 0; 1569 } 1570 if (syncrecv) { 1571 pfd[PFD_SYNCFD].fd = syncfd; 1572 pfd[PFD_SYNCFD].events = POLLIN; 1573 } else { 1574 pfd[PFD_SYNCFD].fd = -1; 1575 pfd[PFD_SYNCFD].events = 0; 1576 } 1577 if (greylist) { 1578 pfd[PFD_GREYBACK].fd = greyback[1]; 1579 pfd[PFD_GREYBACK].events = POLLIN; 1580 } else { 1581 pfd[PFD_GREYBACK].fd = -1; 1582 pfd[PFD_GREYBACK].events = 0; 1583 } 1584 1585 /* events and pfd entries for con[] are filled in below. */ 1586 pfd[PFD_SMTPLISTEN].fd = smtplisten; 1587 pfd[PFD_CONFLISTEN].fd = conflisten; 1588 1589 while (1) { 1590 int numcon = 0, n, timeout, writers; 1591 1592 time(&t); 1593 1594 writers = 0; 1595 for (i = 0; i < maxcon; i++) { 1596 if (con[i].pfd->fd == -1) 1597 continue; 1598 con[i].pfd->events = 0; 1599 if (con[i].r) { 1600 if (con[i].r + MAXTIME <= t) { 1601 closecon(&con[i]); 1602 continue; 1603 } 1604 con[i].pfd->events |= POLLIN; 1605 } 1606 if (con[i].w) { 1607 if (con[i].w + MAXTIME <= t) { 1608 closecon(&con[i]); 1609 continue; 1610 } 1611 if (con[i].w <= t) 1612 con[i].pfd->events |= POLLOUT; 1613 writers = 1; 1614 } 1615 if (con[i].tlsaction == SPAMD_TLS_ACT_READ_POLLIN || 1616 con[i].tlsaction == SPAMD_TLS_ACT_WRITE_POLLIN) 1617 con[i].pfd->events = POLLIN; 1618 if (con[i].tlsaction == SPAMD_TLS_ACT_READ_POLLOUT || 1619 con[i].tlsaction == SPAMD_TLS_ACT_WRITE_POLLOUT) 1620 con[i].pfd->events = POLLOUT; 1621 if (i + 1 > numcon) 1622 numcon = i + 1; 1623 } 1624 pfd[PFD_SMTPLISTEN].events = 0; 1625 pfd[PFD_CONFLISTEN].events = 0; 1626 pfd[PFD_CONFFD].events = 0; 1627 pfd[PFD_CONFFD].fd = conffd; 1628 if (slowdowntill == 0) { 1629 pfd[PFD_SMTPLISTEN].events = POLLIN; 1630 1631 /* only one active config conn at a time */ 1632 if (conffd == -1) 1633 pfd[PFD_CONFLISTEN].events = POLLIN; 1634 else 1635 pfd[PFD_CONFFD].events = POLLIN; 1636 } 1637 1638 /* If we are not listening, wake up at least once a second */ 1639 if (writers == 0 && slowdowntill == 0) 1640 timeout = INFTIM; 1641 else 1642 timeout = 1000; 1643 1644 n = poll(pfd, PFD_FIRSTCON + numcon, timeout); 1645 if (n == -1) { 1646 if (errno != EINTR) 1647 err(1, "poll"); 1648 continue; 1649 } 1650 1651 /* Check if we can speed up accept() calls */ 1652 if (slowdowntill && slowdowntill > t) 1653 slowdowntill = 0; 1654 1655 for (i = 0; i < maxcon; i++) { 1656 if (con[i].pfd->fd == -1) 1657 continue; 1658 if (pfd[PFD_FIRSTCON + i].revents & POLLHUP) { 1659 closecon(&con[i]); 1660 continue; 1661 } 1662 if (pfd[PFD_FIRSTCON + i].revents & POLLIN) { 1663 if (con[i].tlsaction == 1664 SPAMD_TLS_ACT_WRITE_POLLIN) 1665 handlew(&con[i], clients + 5 < maxcon); 1666 else 1667 handler(&con[i]); 1668 } 1669 if (pfd[PFD_FIRSTCON + i].revents & POLLOUT) { 1670 if (con[i].tlsaction == 1671 SPAMD_TLS_ACT_READ_POLLOUT) 1672 handler(&con[i]); 1673 else 1674 handlew(&con[i], clients + 5 < maxcon); 1675 } 1676 } 1677 if (pfd[PFD_SMTPLISTEN].revents & (POLLIN|POLLHUP)) { 1678 socklen_t sinlen; 1679 int s2; 1680 1681 sinlen = sizeof(sin); 1682 s2 = accept4(smtplisten, (struct sockaddr *)&sin, &sinlen, 1683 SOCK_NONBLOCK); 1684 if (s2 == -1) { 1685 switch (errno) { 1686 case EINTR: 1687 case ECONNABORTED: 1688 break; 1689 case EMFILE: 1690 case ENFILE: 1691 slowdowntill = time(NULL) + 1; 1692 break; 1693 default: 1694 errx(1, "accept"); 1695 } 1696 } else { 1697 /* Check if we hit the chosen fd limit */ 1698 for (i = 0; i < maxcon; i++) 1699 if (con[i].pfd->fd == -1) 1700 break; 1701 if (i == maxcon) { 1702 close(s2); 1703 slowdowntill = 0; 1704 } else { 1705 initcon(&con[i], s2, 1706 (struct sockaddr *)&sin); 1707 syslog_r(LOG_INFO, &sdata, 1708 "%s: connected (%d/%d)%s%s", 1709 con[i].addr, clients, blackcount, 1710 ((con[i].lists == NULL) ? "" : 1711 ", lists:"), 1712 ((con[i].lists == NULL) ? "": 1713 con[i].lists)); 1714 } 1715 } 1716 } 1717 if (pfd[PFD_CONFLISTEN].revents & (POLLIN|POLLHUP)) { 1718 socklen_t sinlen; 1719 1720 sinlen = sizeof(lin); 1721 conffd = accept(conflisten, (struct sockaddr *)&lin, 1722 &sinlen); 1723 if (conffd == -1) { 1724 switch (errno) { 1725 case EINTR: 1726 case ECONNABORTED: 1727 break; 1728 case EMFILE: 1729 case ENFILE: 1730 slowdowntill = time(NULL) + 1; 1731 break; 1732 default: 1733 errx(1, "accept"); 1734 } 1735 } else if (ntohs(lin.sin_port) >= IPPORT_RESERVED) { 1736 close(conffd); 1737 conffd = -1; 1738 slowdowntill = 0; 1739 } 1740 } else if (pfd[PFD_CONFFD].revents & (POLLIN|POLLHUP)) 1741 do_config(); 1742 if (pfd[PFD_TRAPFD].revents & (POLLIN|POLLHUP)) 1743 read_configline(trapcfg); 1744 if (pfd[PFD_SYNCFD].revents & (POLLIN|POLLHUP)) 1745 sync_recv(); 1746 if (pfd[PFD_GREYBACK].revents & (POLLIN|POLLHUP)) 1747 blackcheck(greyback[1]); 1748 } 1749 exit(1); 1750 } 1751 1752 void 1753 blackcheck(int fd) 1754 { 1755 struct sockaddr_storage ss; 1756 ssize_t nread; 1757 void *ia; 1758 char ch; 1759 1760 /* Read sockaddr from greylister and look it up in the blacklists. */ 1761 nread = recv(fd, &ss, sizeof(ss), 0); 1762 if (nread == -1) { 1763 syslog(LOG_ERR, "%s: recv: %m", __func__); 1764 return; 1765 } 1766 if (nread != sizeof(struct sockaddr_in) && 1767 nread != sizeof(struct sockaddr_in6)) { 1768 syslog(LOG_ERR, "%s: invalid size %zd", __func__, nread); 1769 return; 1770 } 1771 if (ss.ss_family == AF_INET) { 1772 ia = &((struct sockaddr_in *)&ss)->sin_addr; 1773 } else if (ss.ss_family == AF_INET6) { 1774 ia = &((struct sockaddr_in6 *)&ss)->sin6_addr; 1775 } else { 1776 syslog(LOG_ERR, "%s: bad family %d", __func__, ss.ss_family); 1777 return; 1778 } 1779 ch = sdl_check(blacklists, ss.ss_family, ia) ? '1' : '0'; 1780 1781 /* Send '1' for match or '0' for no match. */ 1782 if (send(fd, &ch, sizeof(ch), 0) == -1) { 1783 syslog(LOG_ERR, "%s: send: %m", __func__); 1784 return; 1785 } 1786 } 1787