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