GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: libexec/spamd/spamd.c Lines: 0 946 0.0 %
Date: 2017-11-07 Branches: 0 681 0.0 %

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