xref: /openbsd/libexec/spamd/spamd.c (revision 94c8de54)
1 /*	$OpenBSD: spamd.c,v 1.163 2024/05/09 08:35:03 florian 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
usage(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 *
grow_obuf(struct con * cp,int off)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
parse_configline(char * line)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
parse_configs(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
do_config(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
read_configline(FILE * config)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
spamd_tls_init(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
append_error_string(struct con * cp,size_t off,char * fmt,int af,void * ia)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 *
loglists(struct con * cp)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
doreply(struct con * cp)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
setlog(char * p,size_t len,char * f)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
getcaddr(struct con * cp)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
gethelo(char * p,size_t len,char * f)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
initcon(struct con * cp,int fd,struct sockaddr * sa)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 	memset(ctimebuf, 0, sizeof(ctimebuf));
734 	ctime_r(&t, ctimebuf);
735 	ctimebuf[sizeof(ctimebuf) - 2] = '\0'; /* nuke newline */
736 	snprintf(cp->obuf, cp->osize, "220 %s ESMTP %s; %s\r\n",
737 	    hostname, spamd, ctimebuf);
738 	cp->op = cp->obuf;
739 	cp->ol = strlen(cp->op);
740 	cp->w = tt + cp->stutter;
741 	cp->s = tt;
742 	strlcpy(cp->rend, "\n", sizeof cp->rend);
743 	clients++;
744 	if (cp->blacklists != NULL) {
745 		blackcount++;
746 		if (greylist && blackcount > maxblack)
747 			cp->stutter = 0;
748 		cp->lists = strdup(loglists(cp));
749 		if (cp->lists == NULL)
750 			err(1, "malloc");
751 	}
752 	else
753 		cp->lists = NULL;
754 }
755 
756 void
closecon(struct con * cp)757 closecon(struct con *cp)
758 {
759 	time_t tt;
760 
761 	if (cp->cctx) {
762 		tls_close(cp->cctx);
763 		tls_free(cp->cctx);
764 	}
765 	close(cp->pfd->fd);
766 	cp->pfd->fd = -1;
767 
768 	slowdowntill = 0;
769 
770 	time(&tt);
771 	syslog_r(LOG_INFO, &sdata, "%s: disconnected after %lld seconds.%s%s",
772 	    cp->addr, (long long)(tt - cp->s),
773 	    ((cp->lists == NULL) ? "" : " lists:"),
774 	    ((cp->lists == NULL) ? "": cp->lists));
775 	if (debug > 0)
776 		printf("%s connected for %lld seconds.\n", cp->addr,
777 		    (long long)(tt - cp->s));
778 	free(cp->lists);
779 	cp->lists = NULL;
780 	if (cp->blacklists != NULL) {
781 		blackcount--;
782 		free(cp->blacklists);
783 		cp->blacklists = NULL;
784 	}
785 	if (cp->obuf != NULL) {
786 		free(cp->obuf);
787 		cp->obuf = NULL;
788 		cp->osize = 0;
789 	}
790 	clients--;
791 }
792 
793 int
match(const char * s1,const char * s2)794 match(const char *s1, const char *s2)
795 {
796 	return (strncasecmp(s1, s2, strlen(s2)) == 0);
797 }
798 
799 void
nextstate(struct con * cp)800 nextstate(struct con *cp)
801 {
802 	if (match(cp->ibuf, "QUIT") && cp->state < 99) {
803 		snprintf(cp->obuf, cp->osize, "221 %s\r\n", hostname);
804 		cp->op = cp->obuf;
805 		cp->ol = strlen(cp->op);
806 		cp->w = t + cp->stutter;
807 		cp->laststate = cp->state;
808 		cp->state = 99;
809 		return;
810 	}
811 
812 	if (match(cp->ibuf, "RSET") && cp->state > 2 && cp->state < 50) {
813 		snprintf(cp->obuf, cp->osize,
814 		    "250 Ok to start over.\r\n");
815 		cp->op = cp->obuf;
816 		cp->ol = strlen(cp->op);
817 		cp->w = t + cp->stutter;
818 		cp->laststate = cp->state;
819 		cp->state = 2;
820 		return;
821 	}
822 	switch (cp->state) {
823 	case 0:
824 	tlsinitdone:
825 		/* banner sent; wait for input */
826 		cp->ip = cp->ibuf;
827 		cp->il = sizeof(cp->ibuf) - 1;
828 		cp->laststate = cp->state;
829 		cp->state = 1;
830 		cp->r = t;
831 		break;
832 	case 1:
833 		/* received input: parse, and select next state */
834 		if (match(cp->ibuf, "HELO") ||
835 		    match(cp->ibuf, "EHLO")) {
836 			int nextstate = 2;
837 			cp->helo[0] = '\0';
838 			gethelo(cp->helo, sizeof cp->helo, cp->ibuf);
839 			if (cp->helo[0] == '\0') {
840 				nextstate = 0;
841 				snprintf(cp->obuf, cp->osize,
842 				    "501 helo requires domain name.\r\n");
843 			} else {
844 				if (cp->cctx == NULL && tlsctx != NULL &&
845 				    cp->blacklists == NULL &&
846 				    match(cp->ibuf, "EHLO")) {
847 					snprintf(cp->obuf, cp->osize,
848 					    "250-%s\r\n"
849 					    "250-8BITMIME\r\n"
850 					    "250-SMTPUTF8\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_INFO, &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, "(%s) %s: %s -> %s\n",
921 					    cp->blacklists ? "BLACK" : "GREY",
922 					    cp->addr, cp->mail, cp->rcpt);
923 				if (greylist && cp->blacklists == NULL) {
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 && cp->blacklists == NULL &&
954 		    cp->cctx == NULL && 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_SOCKET,
986 			    SO_RCVBUF, &window, sizeof(window)) == -1) {
987 				syslog_r(LOG_DEBUG, &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) {
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_DEBUG, &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_INFO, &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 
1078 void
handler(struct con * cp)1079 handler(struct con *cp)
1080 {
1081 	int end = 0;
1082 	ssize_t n;
1083 
1084 	if (cp->r || cp->tlsaction != SPAMD_TLS_ACT_NONE) {
1085 		if (cp->cctx) {
1086 			cp->tlsaction = SPAMD_TLS_ACT_NONE;
1087 			n = tls_read(cp->cctx, cp->ip, cp->il);
1088 			if (n == TLS_WANT_POLLIN)
1089 				cp->tlsaction = SPAMD_TLS_ACT_READ_POLLIN;
1090 			if (n == TLS_WANT_POLLOUT)
1091 				cp->tlsaction = SPAMD_TLS_ACT_READ_POLLOUT;
1092 			if (cp->tlsaction != SPAMD_TLS_ACT_NONE)
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 == EAGAIN)
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 
1124 void
handlew(struct con * cp,int one)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 (errno == EAGAIN)
1156 					return;
1157 				if (debug > 0 && errno != EPIPE)
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_NONE;
1169 			n = tls_write(cp->cctx, cp->op, cp->ol);
1170 			if (n == TLS_WANT_POLLIN)
1171 				cp->tlsaction = SPAMD_TLS_ACT_WRITE_POLLIN;
1172 			if (n == TLS_WANT_POLLOUT)
1173 				cp->tlsaction = SPAMD_TLS_ACT_WRITE_POLLOUT;
1174 			if (cp->tlsaction != SPAMD_TLS_ACT_NONE)
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 == EAGAIN)
1184 				return;
1185 			if (debug > 0 && errno != EPIPE)
1186 				warn("write");
1187 			closecon(cp);
1188 		} else {
1189 			cp->op += n;
1190 			cp->ol -= n;
1191 		}
1192 	}
1193 handled:
1194 	cp->w = t + cp->stutter;
1195 	if (cp->ol == 0) {
1196 		cp->w = 0;
1197 		nextstate(cp);
1198 	}
1199 }
1200 
1201 static int
get_maxfiles(void)1202 get_maxfiles(void)
1203 {
1204 	int mib[2], maxfiles;
1205 	size_t len;
1206 
1207 	mib[0] = CTL_KERN;
1208 	mib[1] = KERN_MAXFILES;
1209 	len = sizeof(maxfiles);
1210 	if (sysctl(mib, 2, &maxfiles, &len, NULL, 0) == -1)
1211 		return(MAXCON);
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_SMTPLISTEN	0
1221 #define PFD_CONFLISTEN	1
1222 #define PFD_SYNCFD	2
1223 #define PFD_CONFFD	3
1224 #define PFD_TRAPFD	4
1225 #define PFD_GREYBACK	5
1226 #define PFD_FIRSTCON	6
1227 
1228 int
main(int argc,char * argv[])1229 main(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;
1240 	const char *errstr;
1241 	char *sync_iface = NULL;
1242 	char *sync_baddr = NULL;
1243 	struct addrinfo hints, *res;
1244 	char *addr;
1245 	char portstr[6];
1246 	int error;
1247 
1248 	tzset();
1249 	openlog_r("spamd", LOG_PID | LOG_NDELAY, LOG_DAEMON, &sdata);
1250 
1251 	if ((ent = getservbyname("spamd", "tcp")) == NULL)
1252 		errx(1, "Can't find service \"spamd\" in /etc/services");
1253 	port = ntohs(ent->s_port);
1254 	if ((ent = getservbyname("spamd-cfg", "tcp")) == NULL)
1255 		errx(1, "Can't find service \"spamd-cfg\" in /etc/services");
1256 	cfg_port = ntohs(ent->s_port);
1257 	if ((ent = getservbyname("spamd-sync", "udp")) == NULL)
1258 		errx(1, "Can't find service \"spamd-sync\" in /etc/services");
1259 	sync_port = ntohs(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_MAX, &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, "-c %s: %s\n", optarg, errstr);
1289 				usage();
1290 			}
1291 			break;
1292 		case 'p':
1293 			port = strtonum(optarg, 1, USHRT_MAX, &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_MAX, &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)) == NULL)
1382 		errx(1, "no such user %s", SPAMD_USER);
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_NOFILE, &rlp) == -1)
1393 		err(1, "setrlimit");
1394 
1395 	pfd = reallocarray(NULL, PFD_FIRSTCON + maxcon, sizeof(*pfd));
1396 	if (pfd == NULL)
1397 		err(1, "reallocarray");
1398 
1399 	con = calloc(maxcon, sizeof(*con));
1400 	if (con == NULL)
1401 		err(1, "calloc");
1402 
1403 	con->obuf = malloc(8192);
1404 
1405 	if (con->obuf == NULL)
1406 		err(1, "malloc");
1407 	con->osize = 8192;
1408 
1409 	for (i = 0; i < maxcon; i++) {
1410 		con[i].pfd = &pfd[PFD_FIRSTCON + i];
1411 		con[i].pfd->fd = -1;
1412 	}
1413 
1414 	signal(SIGPIPE, SIG_IGN);
1415 
1416 	smtplisten = socket(AF_INET, SOCK_STREAM, 0);
1417 	if (smtplisten == -1)
1418 		err(1, "socket");
1419 
1420 	if (setsockopt(smtplisten, SOL_SOCKET, SO_REUSEADDR, &one,
1421 	    sizeof(one)) == -1)
1422 		return (-1);
1423 
1424 	conflisten = socket(AF_INET, SOCK_STREAM, 0);
1425 	if (conflisten == -1)
1426 		err(1, "socket");
1427 
1428 	if (setsockopt(conflisten, SOL_SOCKET, SO_REUSEADDR, &one,
1429 	    sizeof(one)) == -1)
1430 		return (-1);
1431 
1432 	memset(&hints, 0, sizeof(hints));
1433 	hints.ai_family = AF_INET;
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);
1450 	lin.sin_family = AF_INET;
1451 	lin.sin_port = htons(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_RDWR);
1463 		if (pfdev == -1) {
1464 			syslog_r(LOG_ERR, &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_UNIX, SOCK_DGRAM, 0, greyback) == -1) {
1476 			syslog(LOG_ERR, "socketpair (%m)");
1477 			exit(1);
1478 		}
1479 		if (pipe(greypipe) == -1) {
1480 			syslog(LOG_ERR, "pipe (%m)");
1481 			exit(1);
1482 		}
1483 		/* open pipe to receive spamtrap configs */
1484 		if (pipe(trappipe) == -1) {
1485 			syslog(LOG_ERR, "pipe (%m)");
1486 			exit(1);
1487 		}
1488 		jail_pid = fork();
1489 		switch (jail_pid) {
1490 		case -1:
1491 			syslog(LOG_ERR, "fork (%m)");
1492 			exit(1);
1493 		case 0:
1494 			/* child - continue */
1495 			signal(SIGPIPE, SIG_IGN);
1496 			grey = fdopen(greypipe[1], "w");
1497 			if (grey == NULL) {
1498 				syslog(LOG_ERR, "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) {
1506 				syslog(LOG_ERR, "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) {
1522 			syslog(LOG_ERR, "fdopen (%m)");
1523 			exit(1);
1524 		}
1525 		close(greypipe[1]);
1526 		trapcfg = fdopen(trappipe[1], "w");
1527 		if (trapcfg == NULL) {
1528 			syslog(LOG_ERR, "fdopen (%m)");
1529 			exit(1);
1530 		}
1531 		close(trappipe[0]);
1532 		return (greywatcher());
1533 	}
1534 
1535 jail:
1536 	if (pledge("stdio inet", NULL) == -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_WARNING, &sdata, "listening for incoming connections.");
1548 
1549 	/* We always check for trap and sync events if configured. */
1550 	if (trapfd != -1) {
1551 		pfd[PFD_TRAPFD].fd = trapfd;
1552 		pfd[PFD_TRAPFD].events = POLLIN;
1553 	} else {
1554 		pfd[PFD_TRAPFD].fd = -1;
1555 		pfd[PFD_TRAPFD].events = 0;
1556 	}
1557 	if (syncrecv) {
1558 		pfd[PFD_SYNCFD].fd = syncfd;
1559 		pfd[PFD_SYNCFD].events = POLLIN;
1560 	} else {
1561 		pfd[PFD_SYNCFD].fd = -1;
1562 		pfd[PFD_SYNCFD].events = 0;
1563 	}
1564 	if (greylist) {
1565 		pfd[PFD_GREYBACK].fd = greyback[1];
1566 		pfd[PFD_GREYBACK].events = POLLIN;
1567 	} else {
1568 		pfd[PFD_GREYBACK].fd = -1;
1569 		pfd[PFD_GREYBACK].events = 0;
1570 	}
1571 
1572 	/* events and pfd entries for con[] are filled in below. */
1573 	pfd[PFD_SMTPLISTEN].fd = smtplisten;
1574 	pfd[PFD_CONFLISTEN].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 + MAXTIME <= t) {
1588 					closecon(&con[i]);
1589 					continue;
1590 				}
1591 				con[i].pfd->events |= POLLIN;
1592 			}
1593 			if (con[i].w) {
1594 				if (con[i].w + MAXTIME <= t) {
1595 					closecon(&con[i]);
1596 					continue;
1597 				}
1598 				if (con[i].w <= t)
1599 					con[i].pfd->events |= POLLOUT;
1600 				writers = 1;
1601 			}
1602 			if (con[i].tlsaction == SPAMD_TLS_ACT_READ_POLLIN ||
1603 			    con[i].tlsaction == SPAMD_TLS_ACT_WRITE_POLLIN)
1604 				con[i].pfd->events = POLLIN;
1605 			if (con[i].tlsaction == SPAMD_TLS_ACT_READ_POLLOUT ||
1606 			    con[i].tlsaction == SPAMD_TLS_ACT_WRITE_POLLOUT)
1607 				con[i].pfd->events = POLLOUT;
1608 			if (i + 1 > numcon)
1609 				numcon = i + 1;
1610 		}
1611 		pfd[PFD_SMTPLISTEN].events = 0;
1612 		pfd[PFD_CONFLISTEN].events = 0;
1613 		pfd[PFD_CONFFD].events = 0;
1614 		pfd[PFD_CONFFD].fd = conffd;
1615 		if (slowdowntill == 0) {
1616 			pfd[PFD_SMTPLISTEN].events = POLLIN;
1617 
1618 			/* only one active config conn at a time */
1619 			if (conffd == -1)
1620 				pfd[PFD_CONFLISTEN].events = POLLIN;
1621 			else
1622 				pfd[PFD_CONFFD].events = POLLIN;
1623 		}
1624 
1625 		/* If we are not listening, wake up at least once a second */
1626 		if (writers == 0 && slowdowntill == 0)
1627 			timeout = INFTIM;
1628 		else
1629 			timeout = 1000;
1630 
1631 		n = poll(pfd, PFD_FIRSTCON + numcon, timeout);
1632 		if (n == -1) {
1633 			if (errno != EINTR)
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_FIRSTCON + i].revents & POLLHUP) {
1646 				closecon(&con[i]);
1647 				continue;
1648 			}
1649 			if (pfd[PFD_FIRSTCON + i].revents & POLLIN) {
1650 				if (con[i].tlsaction ==
1651 				    SPAMD_TLS_ACT_WRITE_POLLIN)
1652 					handlew(&con[i], clients + 5 < maxcon);
1653 				else
1654 					handler(&con[i]);
1655 			}
1656 			if (pfd[PFD_FIRSTCON + i].revents & POLLOUT) {
1657 				if (con[i].tlsaction ==
1658 				    SPAMD_TLS_ACT_READ_POLLOUT)
1659 					handler(&con[i]);
1660 				else
1661 					handlew(&con[i], clients + 5 < maxcon);
1662 			}
1663 		}
1664 		if (pfd[PFD_SMTPLISTEN].revents & (POLLIN|POLLHUP)) {
1665 			socklen_t sinlen;
1666 			int s2;
1667 
1668 			sinlen = sizeof(sin);
1669 			s2 = accept4(smtplisten, (struct sockaddr *)&sin, &sinlen,
1670 			    SOCK_NONBLOCK);
1671 			if (s2 == -1) {
1672 				switch (errno) {
1673 				case EINTR:
1674 				case ECONNABORTED:
1675 					break;
1676 				case EMFILE:
1677 				case ENFILE:
1678 					slowdowntill = time(NULL) + 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_INFO, &sdata,
1695 					    "%s: connected (%d/%d)%s%s",
1696 					    con[i].addr, clients, blackcount,
1697 					    ((con[i].lists == NULL) ? "" :
1698 					    ", lists:"),
1699 					    ((con[i].lists == NULL) ? "":
1700 					    con[i].lists));
1701 				}
1702 			}
1703 		}
1704 		if (pfd[PFD_CONFLISTEN].revents & (POLLIN|POLLHUP)) {
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) {
1712 				case EINTR:
1713 				case ECONNABORTED:
1714 					break;
1715 				case EMFILE:
1716 				case ENFILE:
1717 					slowdowntill = time(NULL) + 1;
1718 					break;
1719 				default:
1720 					errx(1, "accept");
1721 				}
1722 			} else if (ntohs(lin.sin_port) >= IPPORT_RESERVED) {
1723 				close(conffd);
1724 				conffd = -1;
1725 				slowdowntill = 0;
1726 			}
1727 		} else if (pfd[PFD_CONFFD].revents & (POLLIN|POLLHUP))
1728 			do_config();
1729 		if (pfd[PFD_TRAPFD].revents & (POLLIN|POLLHUP))
1730 			read_configline(trapcfg);
1731 		if (pfd[PFD_SYNCFD].revents & (POLLIN|POLLHUP))
1732 			sync_recv();
1733 		if (pfd[PFD_GREYBACK].revents & (POLLIN|POLLHUP))
1734 			blackcheck(greyback[1]);
1735 	}
1736 	exit(1);
1737 }
1738 
1739 void
blackcheck(int fd)1740 blackcheck(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_ERR, "%s: recv: %m", __func__);
1751 		return;
1752 	}
1753 	if (nread != sizeof(struct sockaddr_in) &&
1754 	    nread != sizeof(struct sockaddr_in6)) {
1755 		syslog(LOG_ERR, "%s: invalid size %zd", __func__, nread);
1756 		return;
1757 	}
1758 	if (ss.ss_family == AF_INET) {
1759 		ia = &((struct sockaddr_in *)&ss)->sin_addr;
1760 	} else if (ss.ss_family == AF_INET6) {
1761 		ia = &((struct sockaddr_in6 *)&ss)->sin6_addr;
1762 	} else {
1763 		syslog(LOG_ERR, "%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_ERR, "%s: send: %m", __func__);
1771 		return;
1772 	}
1773 }
1774