GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: libexec/spamd/spamd.c Lines: 0 977 0.0 %
Date: 2017-11-13 Branches: 0 701 0.0 %

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