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