Bug Summary

File:src/libexec/spamd/spamd.c
Warning:line 644, column 2
Value stored to 's' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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