xref: /openbsd/usr.sbin/smtpd/lka_filter.c (revision 806da1f1)
1 /*	$OpenBSD: lka_filter.c,v 1.77 2024/05/14 13:38:54 op Exp $	*/
2 
3 /*
4  * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <errno.h>
20 #include <inttypes.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "smtpd.h"
25 #include "log.h"
26 
27 #define	PROTOCOL_VERSION	"0.7"
28 
29 struct filter;
30 struct filter_session;
31 static void	filter_protocol_internal(struct filter_session *, uint64_t *, uint64_t, enum filter_phase, const char *);
32 static void	filter_protocol(uint64_t, enum filter_phase, const char *);
33 static void	filter_protocol_next(uint64_t, uint64_t, enum filter_phase);
34 static void	filter_protocol_query(struct filter *, uint64_t, uint64_t, const char *, const char *);
35 
36 static void	filter_data_internal(struct filter_session *, uint64_t, uint64_t, const char *);
37 static void	filter_data(uint64_t, const char *);
38 static void	filter_data_next(uint64_t, uint64_t, const char *);
39 static void	filter_data_query(struct filter *, uint64_t, uint64_t, const char *);
40 
41 static int	filter_builtins_notimpl(struct filter_session *, struct filter *, uint64_t, const char *);
42 static int	filter_builtins_connect(struct filter_session *, struct filter *, uint64_t, const char *);
43 static int	filter_builtins_helo(struct filter_session *, struct filter *, uint64_t, const char *);
44 static int	filter_builtins_mail_from(struct filter_session *, struct filter *, uint64_t, const char *);
45 static int	filter_builtins_rcpt_to(struct filter_session *, struct filter *, uint64_t, const char *);
46 static int	filter_builtins_data(struct filter_session *, struct filter *, uint64_t, const char *);
47 static int	filter_builtins_commit(struct filter_session *, struct filter *, uint64_t, const char *);
48 
49 static void	filter_result_proceed(uint64_t);
50 static void	filter_result_junk(uint64_t);
51 static void	filter_result_rewrite(uint64_t, const char *);
52 static void	filter_result_reject(uint64_t, const char *);
53 static void	filter_result_disconnect(uint64_t, const char *);
54 
55 static void	filter_session_io(struct io *, int, void *);
56 void		lka_filter_process_response(const char *, const char *);
57 
58 
59 struct filter_session {
60 	uint64_t	id;
61 	struct io	*io;
62 
63 	char *lastparam;
64 
65 	char *filter_name;
66 	struct sockaddr_storage ss_src;
67 	struct sockaddr_storage ss_dest;
68 	char *rdns;
69 	int fcrdns;
70 
71 	char *helo;
72 	char *username;
73 	char *mail_from;
74 
75 	enum filter_phase	phase;
76 };
77 
78 static struct filter_exec {
79 	enum filter_phase	phase;
80 	const char	       *phase_name;
81 	int		       (*func)(struct filter_session *, struct filter *, uint64_t, const char *);
82 } filter_execs[FILTER_PHASES_COUNT] = {
83 	{ FILTER_CONNECT,	"connect",	filter_builtins_connect },
84 	{ FILTER_HELO,		"helo",		filter_builtins_helo },
85 	{ FILTER_EHLO,		"ehlo",		filter_builtins_helo },
86 	{ FILTER_STARTTLS,     	"starttls",	filter_builtins_notimpl },
87 	{ FILTER_AUTH,     	"auth",		filter_builtins_notimpl },
88 	{ FILTER_MAIL_FROM,    	"mail-from",	filter_builtins_mail_from },
89 	{ FILTER_RCPT_TO,    	"rcpt-to",	filter_builtins_rcpt_to },
90 	{ FILTER_DATA,    	"data",		filter_builtins_data },
91 	{ FILTER_DATA_LINE,    	"data-line",   	filter_builtins_notimpl },
92 	{ FILTER_RSET,    	"rset",		filter_builtins_notimpl },
93 	{ FILTER_QUIT,    	"quit",		filter_builtins_notimpl },
94 	{ FILTER_NOOP,    	"noop",		filter_builtins_notimpl },
95 	{ FILTER_HELP,    	"help",		filter_builtins_notimpl },
96 	{ FILTER_WIZ,    	"wiz",		filter_builtins_notimpl },
97 	{ FILTER_COMMIT,    	"commit",      	filter_builtins_commit },
98 };
99 
100 struct filter {
101 	uint64_t		id;
102 	uint32_t		phases;
103 	const char	       *name;
104 	const char	       *proc;
105 	struct filter  	      **chain;
106 	size_t 			chain_size;
107 	struct filter_config   *config;
108 };
109 static struct dict filters;
110 
111 struct filter_entry {
112 	TAILQ_ENTRY(filter_entry)	entries;
113 	uint64_t			id;
114 	const char		       *name;
115 };
116 
117 struct filter_chain {
118 	TAILQ_HEAD(, filter_entry)		chain[nitems(filter_execs)];
119 };
120 
121 static struct tree	sessions;
122 static int		filters_inited;
123 
124 static struct dict	filter_chains;
125 
126 struct reporter_proc {
127 	TAILQ_ENTRY(reporter_proc)	entries;
128 	const char		       *name;
129 };
130 TAILQ_HEAD(reporters, reporter_proc);
131 
132 static struct dict	report_smtp_in;
133 static struct dict	report_smtp_out;
134 
135 static struct smtp_events {
136 	const char     *event;
137 } smtp_events[] = {
138 	{ "link-connect" },
139 	{ "link-disconnect" },
140 	{ "link-greeting" },
141 	{ "link-identify" },
142 	{ "link-tls" },
143 	{ "link-auth" },
144 
145 	{ "tx-reset" },
146 	{ "tx-begin" },
147 	{ "tx-mail" },
148 	{ "tx-rcpt" },
149 	{ "tx-envelope" },
150 	{ "tx-data" },
151 	{ "tx-commit" },
152 	{ "tx-rollback" },
153 
154 	{ "protocol-client" },
155 	{ "protocol-server" },
156 
157 	{ "filter-report" },
158 	{ "filter-response" },
159 
160 	{ "timeout" },
161 };
162 
163 static int			processors_inited = 0;
164 static struct dict		processors;
165 
166 struct processor_instance {
167 	char			*name;
168 	struct io		*io;
169 	struct io		*errfd;
170 	int			 ready;
171 	uint32_t		 subsystems;
172 };
173 
174 static void	processor_io(struct io *, int, void *);
175 static void	processor_errfd(struct io *, int, void *);
176 void		lka_filter_process_response(const char *, const char *);
177 
178 int
lka_proc_ready(void)179 lka_proc_ready(void)
180 {
181 	void	*iter;
182 	struct processor_instance	*pi;
183 
184 	iter = NULL;
185 	while (dict_iter(&processors, &iter, NULL, (void **)&pi))
186 		if (!pi->ready)
187 			return 0;
188 	return 1;
189 }
190 
191 static void
lka_proc_config(struct processor_instance * pi)192 lka_proc_config(struct processor_instance *pi)
193 {
194 	io_printf(pi->io, "config|smtpd-version|%s\n", SMTPD_VERSION);
195 	io_printf(pi->io, "config|protocol|%s\n", PROTOCOL_VERSION);
196 	io_printf(pi->io, "config|smtp-session-timeout|%d\n", SMTPD_SESSION_TIMEOUT);
197 	if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_IN)
198 		io_printf(pi->io, "config|subsystem|smtp-in\n");
199 	if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_OUT)
200 		io_printf(pi->io, "config|subsystem|smtp-out\n");
201 	io_printf(pi->io, "config|admd|%s\n",
202 	    env->sc_admd != NULL ? env->sc_admd : env->sc_hostname);
203 	io_printf(pi->io, "config|ready\n");
204 }
205 
206 void
lka_proc_forked(const char * name,uint32_t subsystems,int fd)207 lka_proc_forked(const char *name, uint32_t subsystems, int fd)
208 {
209 	struct processor_instance	*processor;
210 
211 	if (!processors_inited) {
212 		dict_init(&processors);
213 		processors_inited = 1;
214 	}
215 
216 	processor = xcalloc(1, sizeof *processor);
217 	processor->name = xstrdup(name);
218 	processor->io = io_new();
219 	processor->subsystems = subsystems;
220 
221 	io_set_nonblocking(fd);
222 
223 	io_set_fd(processor->io, fd);
224 	io_set_callback(processor->io, processor_io, processor->name);
225 	dict_xset(&processors, name, processor);
226 }
227 
228 void
lka_proc_errfd(const char * name,int fd)229 lka_proc_errfd(const char *name, int fd)
230 {
231 	struct processor_instance	*processor;
232 
233 	processor = dict_xget(&processors, name);
234 
235 	io_set_nonblocking(fd);
236 
237 	processor->errfd = io_new();
238 	io_set_fd(processor->errfd, fd);
239 	io_set_callback(processor->errfd, processor_errfd, processor->name);
240 
241 	lka_proc_config(processor);
242 }
243 
244 struct io *
lka_proc_get_io(const char * name)245 lka_proc_get_io(const char *name)
246 {
247 	struct processor_instance *processor;
248 
249 	processor = dict_xget(&processors, name);
250 
251 	return processor->io;
252 }
253 
254 static void
processor_register(const char * name,const char * line)255 processor_register(const char *name, const char *line)
256 {
257 	struct processor_instance *processor;
258 
259 	processor = dict_xget(&processors, name);
260 
261 	if (strcmp(line, "register|ready") == 0) {
262 		processor->ready = 1;
263 		return;
264 	}
265 
266 	if (strncmp(line, "register|report|", 16) == 0) {
267 		lka_report_register_hook(name, line+16);
268 		return;
269 	}
270 
271 	if (strncmp(line, "register|filter|", 16) == 0) {
272 		lka_filter_register_hook(name, line+16);
273 		return;
274 	}
275 
276 	fatalx("Invalid register line received: %s", line);
277 }
278 
279 static void
processor_io(struct io * io,int evt,void * arg)280 processor_io(struct io *io, int evt, void *arg)
281 {
282 	struct processor_instance *processor;
283 	const char		*name = arg;
284 	char			*line = NULL;
285 	ssize_t			 len;
286 
287 	switch (evt) {
288 	case IO_DATAIN:
289 		while ((line = io_getline(io, &len)) != NULL) {
290 			if (strncmp("register|", line, 9) == 0) {
291 				processor_register(name, line);
292 				continue;
293 			}
294 
295 			processor = dict_xget(&processors, name);
296 			if (!processor->ready)
297 				fatalx("Non-register message before register|"
298 				    "ready: %s", line);
299 			else if (strncmp(line, "filter-result|", 14) == 0 ||
300 			    strncmp(line, "filter-dataline|", 16) == 0)
301 				lka_filter_process_response(name, line);
302 			else if (strncmp(line, "report|", 7) == 0)
303 				lka_report_proc(name, line);
304 			else
305 				fatalx("Invalid filter message type: %s", line);
306 		}
307 	}
308 }
309 
310 static void
processor_errfd(struct io * io,int evt,void * arg)311 processor_errfd(struct io *io, int evt, void *arg)
312 {
313 	const char	*name = arg;
314 	char		*line = NULL;
315 	ssize_t		 len;
316 
317 	switch (evt) {
318 	case IO_DATAIN:
319 		while ((line = io_getline(io, &len)) != NULL)
320 			log_warnx("%s: %s", name, line);
321 	}
322 }
323 
324 void
lka_filter_init(void)325 lka_filter_init(void)
326 {
327 	void		*iter;
328 	const char	*name;
329 	struct filter  	*filter;
330 	struct filter_config	*filter_config;
331 	size_t		i;
332 	char		 buffer[LINE_MAX];	/* for traces */
333 
334 	dict_init(&filters);
335 	dict_init(&filter_chains);
336 
337 	/* first pass, allocate and init individual filters */
338 	iter = NULL;
339 	while (dict_iter(env->sc_filters_dict, &iter, &name, (void **)&filter_config)) {
340 		switch (filter_config->filter_type) {
341 		case FILTER_TYPE_BUILTIN:
342 			filter = xcalloc(1, sizeof(*filter));
343 			filter->name = name;
344 			filter->phases |= (1<<filter_config->phase);
345 			filter->config = filter_config;
346 			dict_set(&filters, name, filter);
347 			log_trace(TRACE_FILTERS, "filters init type=builtin, name=%s, hooks=%08x",
348 			    name, filter->phases);
349 			break;
350 
351 		case FILTER_TYPE_PROC:
352 			filter = xcalloc(1, sizeof(*filter));
353 			filter->name = name;
354 			filter->proc = filter_config->proc;
355 			filter->config = filter_config;
356 			dict_set(&filters, name, filter);
357 			log_trace(TRACE_FILTERS, "filters init type=proc, name=%s, proc=%s",
358 			    name, filter_config->proc);
359 			break;
360 
361 		case FILTER_TYPE_CHAIN:
362 			break;
363 		}
364 	}
365 
366 	/* second pass, allocate and init filter chains but don't build yet */
367 	iter = NULL;
368 	while (dict_iter(env->sc_filters_dict, &iter, &name, (void **)&filter_config)) {
369 		switch (filter_config->filter_type) {
370 		case FILTER_TYPE_CHAIN:
371 			filter = xcalloc(1, sizeof(*filter));
372 			filter->name = name;
373 			filter->chain = xcalloc(filter_config->chain_size, sizeof(void **));
374 			filter->chain_size = filter_config->chain_size;
375 			filter->config = filter_config;
376 
377 			buffer[0] = '\0';
378 			for (i = 0; i < filter->chain_size; ++i) {
379 				filter->chain[i] = dict_xget(&filters, filter_config->chain[i]);
380 				if (i)
381 					(void)strlcat(buffer, ", ", sizeof buffer);
382 				(void)strlcat(buffer, filter->chain[i]->name, sizeof buffer);
383 			}
384 			log_trace(TRACE_FILTERS, "filters init type=chain, name=%s { %s }", name, buffer);
385 
386 			dict_set(&filters, name, filter);
387 			break;
388 
389 		case FILTER_TYPE_BUILTIN:
390 		case FILTER_TYPE_PROC:
391 			break;
392 		}
393 	}
394 }
395 
396 void
lka_filter_register_hook(const char * name,const char * hook)397 lka_filter_register_hook(const char *name, const char *hook)
398 {
399 	struct filter		*filter;
400 	const char	*filter_name;
401 	void		*iter;
402 	size_t	i;
403 
404 	if (strncasecmp(hook, "smtp-in|", 8) == 0) {
405 		hook += 8;
406 	}
407 	else
408 		fatalx("Invalid message direction: %s", hook);
409 
410 	for (i = 0; i < nitems(filter_execs); i++)
411 		if (strcmp(hook, filter_execs[i].phase_name) == 0)
412 			break;
413 	if (i == nitems(filter_execs))
414 		fatalx("Unrecognized report name: %s", hook);
415 
416 	iter = NULL;
417 	while (dict_iter(&filters, &iter, &filter_name, (void **)&filter))
418 		if (filter->proc && strcmp(name, filter->proc) == 0)
419 			filter->phases |= (1<<filter_execs[i].phase);
420 }
421 
422 void
lka_filter_ready(void)423 lka_filter_ready(void)
424 {
425 	struct filter  	*filter;
426 	struct filter  	*subfilter;
427 	const char	*filter_name;
428 	struct filter_entry	*filter_entry;
429 	struct filter_chain	*filter_chain;
430 	void		*iter;
431 	size_t		i;
432 	size_t		j;
433 
434 	/* all filters are ready, actually build the filter chains */
435 	iter = NULL;
436 	while (dict_iter(&filters, &iter, &filter_name, (void **)&filter)) {
437 		filter_chain = xcalloc(1, sizeof *filter_chain);
438 		for (i = 0; i < nitems(filter_execs); i++)
439 			TAILQ_INIT(&filter_chain->chain[i]);
440 		dict_set(&filter_chains, filter_name, filter_chain);
441 
442 		if (filter->chain) {
443 			for (i = 0; i < filter->chain_size; i++) {
444 				subfilter = filter->chain[i];
445 				for (j = 0; j < nitems(filter_execs); ++j) {
446 					if (subfilter->phases & (1<<j)) {
447 						filter_entry = xcalloc(1, sizeof *filter_entry);
448 						filter_entry->id = generate_uid();
449 						filter_entry->name = subfilter->name;
450 						TAILQ_INSERT_TAIL(&filter_chain->chain[j],
451 						    filter_entry, entries);
452 					}
453 				}
454 			}
455 			continue;
456 		}
457 
458 		for (i = 0; i < nitems(filter_execs); ++i) {
459 			if (filter->phases & (1<<i)) {
460 				filter_entry = xcalloc(1, sizeof *filter_entry);
461 				filter_entry->id = generate_uid();
462 				filter_entry->name = filter_name;
463 				TAILQ_INSERT_TAIL(&filter_chain->chain[i],
464 				    filter_entry, entries);
465 			}
466 		}
467 	}
468 }
469 
470 int
lka_filter_proc_in_session(uint64_t reqid,const char * proc)471 lka_filter_proc_in_session(uint64_t reqid, const char *proc)
472 {
473 	struct filter_session	*fs;
474 	struct filter		*filter;
475 	size_t			 i;
476 
477 	if ((fs = tree_get(&sessions, reqid)) == NULL)
478 		return 0;
479 
480 	filter = dict_get(&filters, fs->filter_name);
481 	if (filter == NULL || (filter->proc == NULL && filter->chain == NULL))
482 		return 0;
483 
484 	if (filter->proc)
485 		return strcmp(filter->proc, proc) == 0 ? 1 : 0;
486 
487 	for (i = 0; i < filter->chain_size; i++)
488 		if (filter->chain[i]->proc &&
489 		    strcmp(filter->chain[i]->proc, proc) == 0)
490 			return 1;
491 
492 	return 0;
493 }
494 
495 void
lka_filter_begin(uint64_t reqid,const char * filter_name)496 lka_filter_begin(uint64_t reqid, const char *filter_name)
497 {
498 	struct filter_session	*fs;
499 
500 	if (!filters_inited) {
501 		tree_init(&sessions);
502 		filters_inited = 1;
503 	}
504 
505 	fs = xcalloc(1, sizeof (struct filter_session));
506 	fs->id = reqid;
507 	fs->filter_name = xstrdup(filter_name);
508 	tree_xset(&sessions, fs->id, fs);
509 
510 	log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-begin", reqid);
511 }
512 
513 void
lka_filter_end(uint64_t reqid)514 lka_filter_end(uint64_t reqid)
515 {
516 	struct filter_session	*fs;
517 
518 	fs = tree_xpop(&sessions, reqid);
519 	free(fs->rdns);
520 	free(fs->helo);
521 	free(fs->mail_from);
522 	free(fs->username);
523 	free(fs->lastparam);
524 	free(fs->filter_name);
525 	free(fs);
526 	log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-end", reqid);
527 }
528 
529 void
lka_filter_data_begin(uint64_t reqid)530 lka_filter_data_begin(uint64_t reqid)
531 {
532 	struct filter_session  *fs;
533 	int	sp[2];
534 	int	fd = -1;
535 
536 	fs = tree_xget(&sessions, reqid);
537 
538 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1)
539 		goto end;
540 	io_set_nonblocking(sp[0]);
541 	io_set_nonblocking(sp[1]);
542 	fd = sp[0];
543 	fs->io = io_new();
544 	io_set_fd(fs->io, sp[1]);
545 	io_set_callback(fs->io, filter_session_io, fs);
546 
547 end:
548 	m_create(p_dispatcher, IMSG_FILTER_SMTP_DATA_BEGIN, 0, 0, fd);
549 	m_add_id(p_dispatcher, reqid);
550 	m_add_int(p_dispatcher, fd != -1 ? 1 : 0);
551 	m_close(p_dispatcher);
552 	log_trace(TRACE_FILTERS, "%016"PRIx64" filters data-begin fd=%d", reqid, fd);
553 }
554 
555 void
lka_filter_data_end(uint64_t reqid)556 lka_filter_data_end(uint64_t reqid)
557 {
558 	struct filter_session	*fs;
559 
560 	fs = tree_xget(&sessions, reqid);
561 	if (fs->io) {
562 		io_free(fs->io);
563 		fs->io = NULL;
564 	}
565 	log_trace(TRACE_FILTERS, "%016"PRIx64" filters data-end", reqid);
566 }
567 
568 static void
filter_session_io(struct io * io,int evt,void * arg)569 filter_session_io(struct io *io, int evt, void *arg)
570 {
571 	struct filter_session *fs = arg;
572 	char *line = NULL;
573 	ssize_t len;
574 
575 	log_trace(TRACE_IO, "filter session: %p: %s %s", fs, io_strevent(evt),
576 	    io_strio(io));
577 
578 	switch (evt) {
579 	case IO_DATAIN:
580 	nextline:
581 		line = io_getline(fs->io, &len);
582 		/* No complete line received */
583 		if (line == NULL)
584 			return;
585 
586 		filter_data(fs->id, line);
587 
588 		goto nextline;
589 	}
590 }
591 
592 void
lka_filter_process_response(const char * name,const char * line)593 lka_filter_process_response(const char *name, const char *line)
594 {
595 	uint64_t reqid;
596 	uint64_t token;
597 	char *ep = NULL;
598 	const char *kind = NULL;
599 	const char *qid = NULL;
600 	const char *response = NULL;
601 	const char *parameter = NULL;
602 	struct filter_session *fs;
603 
604 	kind = line;
605 
606 	if ((ep = strchr(kind, '|')) == NULL)
607 		fatalx("Missing token: %s", line);
608 	qid = ep+1;
609 
610 	errno = 0;
611 	reqid = strtoull(qid, &ep, 16);
612 	if (qid[0] == '\0' || *ep != '|')
613 		fatalx("Invalid reqid: %s", line);
614 	if (errno == ERANGE && reqid == ULLONG_MAX)
615 		fatal("Invalid reqid: %s", line);
616 
617 	qid = ep + 1;
618 	token = strtoull(qid, &ep, 16);
619 	if (qid[0] == '\0' || *ep != '|')
620 		fatalx("Invalid token: %s", line);
621 	if (errno == ERANGE && token == ULLONG_MAX)
622 		fatal("Invalid token: %s", line);
623 
624 	response = ep+1;
625 
626 	/* session can legitimately disappear on a resume */
627 	if ((fs = tree_get(&sessions, reqid)) == NULL)
628 		return;
629 
630 	if (strncmp(kind, "filter-dataline|", 16) == 0) {
631 		if (fs->phase != FILTER_DATA_LINE)
632 			fatalx("filter-dataline out of dataline phase");
633 		filter_data_next(token, reqid, response);
634 		return;
635 	}
636 	if (fs->phase == FILTER_DATA_LINE)
637 		fatalx("filter-result in dataline phase");
638 
639 	if ((ep = strchr(response, '|')) != NULL)
640 		parameter = ep + 1;
641 
642 	if (strcmp(response, "proceed") == 0) {
643 		filter_protocol_next(token, reqid, 0);
644 		return;
645 	} else if (strcmp(response, "junk") == 0) {
646 		if (fs->phase == FILTER_COMMIT)
647 			fatalx("filter-reponse junk after DATA");
648 		filter_result_junk(reqid);
649 		return;
650 	} else {
651 		if (parameter == NULL)
652 			fatalx("Missing parameter: %s", line);
653 
654 		if (strncmp(response, "rewrite|", 8) == 0)
655 			filter_result_rewrite(reqid, parameter);
656 		else if (strncmp(response, "reject|", 7) == 0)
657 			filter_result_reject(reqid, parameter);
658 		else if (strncmp(response, "disconnect|", 11) == 0)
659 			filter_result_disconnect(reqid, parameter);
660 		else
661 			fatalx("Invalid directive: %s", line);
662 	}
663 }
664 
665 void
lka_filter_protocol(uint64_t reqid,enum filter_phase phase,const char * param)666 lka_filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param)
667 {
668 	filter_protocol(reqid, phase, param);
669 }
670 
671 static void
filter_protocol_internal(struct filter_session * fs,uint64_t * token,uint64_t reqid,enum filter_phase phase,const char * param)672 filter_protocol_internal(struct filter_session *fs, uint64_t *token, uint64_t reqid, enum filter_phase phase, const char *param)
673 {
674 	struct filter_chain	*filter_chain;
675 	struct filter_entry	*filter_entry;
676 	struct filter		*filter;
677 	struct timeval		 tv;
678 	const char		*phase_name = filter_execs[phase].phase_name;
679 	int			 resume = 1;
680 
681 	if (!*token) {
682 		fs->phase = phase;
683 		resume = 0;
684 	}
685 
686 	/* XXX - this sanity check requires a protocol change, stub for now */
687 	phase = fs->phase;
688 	if (fs->phase != phase)
689 		fatalx("misbehaving filter");
690 
691 	/* based on token, identify the filter_entry we should apply  */
692 	filter_chain = dict_get(&filter_chains, fs->filter_name);
693 	filter_entry = TAILQ_FIRST(&filter_chain->chain[fs->phase]);
694 	if (*token) {
695 		TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries)
696 		    if (filter_entry->id == *token)
697 			    break;
698 		if (filter_entry == NULL)
699 			fatalx("misbehaving filter");
700 		filter_entry = TAILQ_NEXT(filter_entry, entries);
701 	}
702 
703 	/* no filter_entry, we either had none or reached end of chain */
704 	if (filter_entry == NULL) {
705 		log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, resume=%s, "
706 		    "action=proceed",
707 		    fs->id, phase_name, resume ? "y" : "n");
708 		filter_result_proceed(reqid);
709 		return;
710 	}
711 
712 	/* process param with current filter_entry */
713 	*token = filter_entry->id;
714 	filter = dict_get(&filters, filter_entry->name);
715 	if (filter->proc) {
716 		log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
717 		    "resume=%s, action=deferred, filter=%s",
718 		    fs->id, phase_name, resume ? "y" : "n",
719 		    filter->name);
720 		filter_protocol_query(filter, filter_entry->id, reqid,
721 		    filter_execs[fs->phase].phase_name, param);
722 		return;	/* deferred response */
723 	}
724 
725 	if (filter_execs[fs->phase].func(fs, filter, reqid, param)) {
726 		if (filter->config->rewrite) {
727 			log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
728 			    "resume=%s, action=rewrite, filter=%s, query=%s, response=%s",
729 			    fs->id, phase_name, resume ? "y" : "n",
730 			    filter->name,
731 			    param,
732 			    filter->config->rewrite);
733 			filter_result_rewrite(reqid, filter->config->rewrite);
734 			return;
735 		}
736 		else if (filter->config->disconnect) {
737 			log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
738 			    "resume=%s, action=disconnect, filter=%s, query=%s, response=%s",
739 			    fs->id, phase_name, resume ? "y" : "n",
740 			    filter->name,
741 			    param,
742 			    filter->config->disconnect);
743 			filter_result_disconnect(reqid, filter->config->disconnect);
744 			return;
745 		}
746 		else if (filter->config->junk) {
747 			log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
748 			    "resume=%s, action=junk, filter=%s, query=%s",
749 			    fs->id, phase_name, resume ? "y" : "n",
750 			    filter->name,
751 			    param);
752 			filter_result_junk(reqid);
753 			return;
754 		} else if (filter->config->report) {
755 			log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
756 			    "resume=%s, action=report, filter=%s, query=%s response=%s",
757 			    fs->id, phase_name, resume ? "y" : "n",
758 			    filter->name,
759 			    param, filter->config->report);
760 
761 			gettimeofday(&tv, NULL);
762 			lka_report_filter_report(fs->id, filter->name, 1,
763 			    "smtp-in", &tv, filter->config->report);
764 		} else if (filter->config->bypass) {
765 			log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
766 			    "resume=%s, action=bypass, filter=%s, query=%s",
767 			    fs->id, phase_name, resume ? "y" : "n",
768 			    filter->name,
769 			    param);
770 			filter_result_proceed(reqid);
771 			return;
772 		} else {
773 			log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
774 			    "resume=%s, action=reject, filter=%s, query=%s, response=%s",
775 			    fs->id, phase_name, resume ? "y" : "n",
776 			    filter->name,
777 			    param,
778 			    filter->config->reject);
779 			filter_result_reject(reqid, filter->config->reject);
780 			return;
781 		}
782 	}
783 
784 	log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
785 	    "resume=%s, action=proceed, filter=%s, query=%s",
786 	    fs->id, phase_name, resume ? "y" : "n",
787 	    filter->name,
788 	    param);
789 
790 	/* filter_entry resulted in proceed, try next filter */
791 	filter_protocol_internal(fs, token, reqid, phase, param);
792 	return;
793 }
794 
795 static void
filter_data_internal(struct filter_session * fs,uint64_t token,uint64_t reqid,const char * line)796 filter_data_internal(struct filter_session *fs, uint64_t token, uint64_t reqid, const char *line)
797 {
798 	struct filter_chain	*filter_chain;
799 	struct filter_entry	*filter_entry;
800 	struct filter		*filter;
801 
802 	if (!token)
803 		fs->phase = FILTER_DATA_LINE;
804 	if (fs->phase != FILTER_DATA_LINE)
805 		fatalx("misbehaving filter");
806 
807 	/* based on token, identify the filter_entry we should apply  */
808 	filter_chain = dict_get(&filter_chains, fs->filter_name);
809 	filter_entry = TAILQ_FIRST(&filter_chain->chain[fs->phase]);
810 	if (token) {
811 		TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries)
812 		    if (filter_entry->id == token)
813 			    break;
814 		if (filter_entry == NULL)
815 			fatalx("misbehaving filter");
816 		filter_entry = TAILQ_NEXT(filter_entry, entries);
817 	}
818 
819 	/* no filter_entry, we either had none or reached end of chain */
820 	if (filter_entry == NULL) {
821 		io_printf(fs->io, "%s\n", line);
822 		return;
823 	}
824 
825 	/* pass data to the filter */
826 	filter = dict_get(&filters, filter_entry->name);
827 	filter_data_query(filter, filter_entry->id, reqid, line);
828 }
829 
830 static void
filter_protocol(uint64_t reqid,enum filter_phase phase,const char * param)831 filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param)
832 {
833 	struct filter_session  *fs;
834 	uint64_t		token = 0;
835 	char		       *nparam = NULL;
836 
837 	fs = tree_xget(&sessions, reqid);
838 
839 	switch (phase) {
840 	case FILTER_HELO:
841 	case FILTER_EHLO:
842 		free(fs->helo);
843 		fs->helo = xstrdup(param);
844 		break;
845 	case FILTER_MAIL_FROM:
846 		free(fs->mail_from);
847 		fs->mail_from = xstrdup(param + 1);
848 		*strchr(fs->mail_from, '>') = '\0';
849 		param = fs->mail_from;
850 
851 		break;
852 	case FILTER_RCPT_TO:
853 		nparam = xstrdup(param + 1);
854 		*strchr(nparam, '>') = '\0';
855 		param = nparam;
856 		break;
857 	case FILTER_STARTTLS:
858 		/* TBD */
859 		break;
860 	default:
861 		break;
862 	}
863 
864 	free(fs->lastparam);
865 	fs->lastparam = xstrdup(param);
866 
867 	filter_protocol_internal(fs, &token, reqid, phase, param);
868 	if (nparam)
869 		free(nparam);
870 }
871 
872 static void
filter_protocol_next(uint64_t token,uint64_t reqid,enum filter_phase phase)873 filter_protocol_next(uint64_t token, uint64_t reqid, enum filter_phase phase)
874 {
875 	struct filter_session  *fs;
876 
877 	/* session can legitimately disappear on a resume */
878 	if ((fs = tree_get(&sessions, reqid)) == NULL)
879 		return;
880 
881 	filter_protocol_internal(fs, &token, reqid, phase, fs->lastparam);
882 }
883 
884 static void
filter_data(uint64_t reqid,const char * line)885 filter_data(uint64_t reqid, const char *line)
886 {
887 	struct filter_session  *fs;
888 
889 	fs = tree_xget(&sessions, reqid);
890 
891 	filter_data_internal(fs, 0, reqid, line);
892 }
893 
894 static void
filter_data_next(uint64_t token,uint64_t reqid,const char * line)895 filter_data_next(uint64_t token, uint64_t reqid, const char *line)
896 {
897 	struct filter_session  *fs;
898 
899 	/* session can legitimately disappear on a resume */
900 	if ((fs = tree_get(&sessions, reqid)) == NULL)
901 		return;
902 
903 	filter_data_internal(fs, token, reqid, line);
904 }
905 
906 static void
filter_protocol_query(struct filter * filter,uint64_t token,uint64_t reqid,const char * phase,const char * param)907 filter_protocol_query(struct filter *filter, uint64_t token, uint64_t reqid, const char *phase, const char *param)
908 {
909 	int	n;
910 	struct filter_session	*fs;
911 	struct timeval	tv;
912 
913 	gettimeofday(&tv, NULL);
914 
915 	fs = tree_xget(&sessions, reqid);
916 	if (strcmp(phase, "connect") == 0)
917 		n = io_printf(lka_proc_get_io(filter->proc),
918 		    "filter|%s|%lld.%06ld|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s|%s\n",
919 		    PROTOCOL_VERSION,
920 		    (long long)tv.tv_sec, (long)tv.tv_usec,
921 		    phase, reqid, token, fs->rdns, param);
922 	else
923 		n = io_printf(lka_proc_get_io(filter->proc),
924 		    "filter|%s|%lld.%06ld|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s\n",
925 		    PROTOCOL_VERSION,
926 		    (long long)tv.tv_sec, (long)tv.tv_usec,
927 		    phase, reqid, token, param);
928 	if (n == -1)
929 		fatalx("failed to write to processor");
930 }
931 
932 static void
filter_data_query(struct filter * filter,uint64_t token,uint64_t reqid,const char * line)933 filter_data_query(struct filter *filter, uint64_t token, uint64_t reqid, const char *line)
934 {
935 	int	n;
936 	struct timeval	tv;
937 
938 	gettimeofday(&tv, NULL);
939 
940 	n = io_printf(lka_proc_get_io(filter->proc),
941 	    "filter|%s|%lld.%06ld|smtp-in|data-line|"
942 	    "%016"PRIx64"|%016"PRIx64"|%s\n",
943 	    PROTOCOL_VERSION,
944 	    (long long)tv.tv_sec, (long)tv.tv_usec,
945 	    reqid, token, line);
946 	if (n == -1)
947 		fatalx("failed to write to processor");
948 }
949 
950 static void
filter_result_proceed(uint64_t reqid)951 filter_result_proceed(uint64_t reqid)
952 {
953 	m_create(p_dispatcher, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
954 	m_add_id(p_dispatcher, reqid);
955 	m_add_int(p_dispatcher, FILTER_PROCEED);
956 	m_close(p_dispatcher);
957 }
958 
959 static void
filter_result_junk(uint64_t reqid)960 filter_result_junk(uint64_t reqid)
961 {
962 	m_create(p_dispatcher, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
963 	m_add_id(p_dispatcher, reqid);
964 	m_add_int(p_dispatcher, FILTER_JUNK);
965 	m_close(p_dispatcher);
966 }
967 
968 static void
filter_result_rewrite(uint64_t reqid,const char * param)969 filter_result_rewrite(uint64_t reqid, const char *param)
970 {
971 	m_create(p_dispatcher, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
972 	m_add_id(p_dispatcher, reqid);
973 	m_add_int(p_dispatcher, FILTER_REWRITE);
974 	m_add_string(p_dispatcher, param);
975 	m_close(p_dispatcher);
976 }
977 
978 static void
filter_result_reject(uint64_t reqid,const char * message)979 filter_result_reject(uint64_t reqid, const char *message)
980 {
981 	m_create(p_dispatcher, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
982 	m_add_id(p_dispatcher, reqid);
983 	m_add_int(p_dispatcher, FILTER_REJECT);
984 	m_add_string(p_dispatcher, message);
985 	m_close(p_dispatcher);
986 }
987 
988 static void
filter_result_disconnect(uint64_t reqid,const char * message)989 filter_result_disconnect(uint64_t reqid, const char *message)
990 {
991 	m_create(p_dispatcher, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
992 	m_add_id(p_dispatcher, reqid);
993 	m_add_int(p_dispatcher, FILTER_DISCONNECT);
994 	m_add_string(p_dispatcher, message);
995 	m_close(p_dispatcher);
996 }
997 
998 
999 /* below is code for builtin filters */
1000 
1001 static int
filter_check_rdns_table(struct filter * filter,enum table_service kind,const char * key)1002 filter_check_rdns_table(struct filter *filter, enum table_service kind, const char *key)
1003 {
1004 	int	ret = 0;
1005 
1006 	if (filter->config->rdns_table == NULL)
1007 		return 0;
1008 
1009 	if (table_match(filter->config->rdns_table, kind, key) > 0)
1010 		ret = 1;
1011 
1012 	return filter->config->not_rdns_table < 0 ? !ret : ret;
1013 }
1014 
1015 static int
filter_check_rdns_regex(struct filter * filter,const char * key)1016 filter_check_rdns_regex(struct filter *filter, const char *key)
1017 {
1018 	int	ret = 0;
1019 
1020 	if (filter->config->rdns_regex == NULL)
1021 		return 0;
1022 
1023 	if (table_match(filter->config->rdns_regex, K_REGEX, key) > 0)
1024 		ret = 1;
1025 	return filter->config->not_rdns_regex < 0 ? !ret : ret;
1026 }
1027 
1028 static int
filter_check_src_table(struct filter * filter,enum table_service kind,const char * key)1029 filter_check_src_table(struct filter *filter, enum table_service kind, const char *key)
1030 {
1031 	int	ret = 0;
1032 
1033 	if (filter->config->src_table == NULL)
1034 		return 0;
1035 
1036 	if (table_match(filter->config->src_table, kind, key) > 0)
1037 		ret = 1;
1038 	return filter->config->not_src_table < 0 ? !ret : ret;
1039 }
1040 
1041 static int
filter_check_src_regex(struct filter * filter,const char * key)1042 filter_check_src_regex(struct filter *filter, const char *key)
1043 {
1044 	int	ret = 0;
1045 
1046 	if (filter->config->src_regex == NULL)
1047 		return 0;
1048 
1049 	if (table_match(filter->config->src_regex, K_REGEX, key) > 0)
1050 		ret = 1;
1051 	return filter->config->not_src_regex < 0 ? !ret : ret;
1052 }
1053 
1054 static int
filter_check_helo_table(struct filter * filter,enum table_service kind,const char * key)1055 filter_check_helo_table(struct filter *filter, enum table_service kind, const char *key)
1056 {
1057 	int	ret = 0;
1058 
1059 	if (filter->config->helo_table == NULL)
1060 		return 0;
1061 
1062 	if (table_match(filter->config->helo_table, kind, key) > 0)
1063 		ret = 1;
1064 	return filter->config->not_helo_table < 0 ? !ret : ret;
1065 }
1066 
1067 static int
filter_check_helo_regex(struct filter * filter,const char * key)1068 filter_check_helo_regex(struct filter *filter, const char *key)
1069 {
1070 	int	ret = 0;
1071 
1072 	if (filter->config->helo_regex == NULL)
1073 		return 0;
1074 
1075 	if (table_match(filter->config->helo_regex, K_REGEX, key) > 0)
1076 		ret = 1;
1077 	return filter->config->not_helo_regex < 0 ? !ret : ret;
1078 }
1079 
1080 static int
filter_check_auth(struct filter * filter,const char * username)1081 filter_check_auth(struct filter *filter, const char *username)
1082 {
1083 	int ret = 0;
1084 
1085 	if (!filter->config->auth)
1086 		return 0;
1087 
1088 	ret = username ? 1 : 0;
1089 
1090 	return filter->config->not_auth < 0 ? !ret : ret;
1091 }
1092 
1093 static int
filter_check_auth_table(struct filter * filter,enum table_service kind,const char * key)1094 filter_check_auth_table(struct filter *filter, enum table_service kind, const char *key)
1095 {
1096 	int	ret = 0;
1097 
1098 	if (filter->config->auth_table == NULL)
1099 		return 0;
1100 
1101 	if (key && table_match(filter->config->auth_table, kind, key) > 0)
1102 		ret = 1;
1103 
1104 	return filter->config->not_auth_table < 0 ? !ret : ret;
1105 }
1106 
1107 static int
filter_check_auth_regex(struct filter * filter,const char * key)1108 filter_check_auth_regex(struct filter *filter, const char *key)
1109 {
1110 	int	ret = 0;
1111 
1112 	if (filter->config->auth_regex == NULL)
1113 		return 0;
1114 
1115 	if (key && table_match(filter->config->auth_regex, K_REGEX, key) > 0)
1116 		ret = 1;
1117 	return filter->config->not_auth_regex < 0 ? !ret : ret;
1118 }
1119 
1120 
1121 static int
filter_check_mail_from_table(struct filter * filter,enum table_service kind,const char * key)1122 filter_check_mail_from_table(struct filter *filter, enum table_service kind, const char *key)
1123 {
1124 	int	ret = 0;
1125 
1126 	if (filter->config->mail_from_table == NULL)
1127 		return 0;
1128 
1129 	if (table_match(filter->config->mail_from_table, kind, key) > 0)
1130 		ret = 1;
1131 	return filter->config->not_mail_from_table < 0 ? !ret : ret;
1132 }
1133 
1134 static int
filter_check_mail_from_regex(struct filter * filter,const char * key)1135 filter_check_mail_from_regex(struct filter *filter, const char *key)
1136 {
1137 	int	ret = 0;
1138 
1139 	if (filter->config->mail_from_regex == NULL)
1140 		return 0;
1141 
1142 	if (table_match(filter->config->mail_from_regex, K_REGEX, key) > 0)
1143 		ret = 1;
1144 	return filter->config->not_mail_from_regex < 0 ? !ret : ret;
1145 }
1146 
1147 static int
filter_check_rcpt_to_table(struct filter * filter,enum table_service kind,const char * key)1148 filter_check_rcpt_to_table(struct filter *filter, enum table_service kind, const char *key)
1149 {
1150 	int	ret = 0;
1151 
1152 	if (filter->config->rcpt_to_table == NULL)
1153 		return 0;
1154 
1155 	if (table_match(filter->config->rcpt_to_table, kind, key) > 0)
1156 		ret = 1;
1157 	return filter->config->not_rcpt_to_table < 0 ? !ret : ret;
1158 }
1159 
1160 static int
filter_check_rcpt_to_regex(struct filter * filter,const char * key)1161 filter_check_rcpt_to_regex(struct filter *filter, const char *key)
1162 {
1163 	int	ret = 0;
1164 
1165 	if (filter->config->rcpt_to_regex == NULL)
1166 		return 0;
1167 
1168 	if (table_match(filter->config->rcpt_to_regex, K_REGEX, key) > 0)
1169 		ret = 1;
1170 	return filter->config->not_rcpt_to_regex < 0 ? !ret : ret;
1171 }
1172 
1173 static int
filter_check_fcrdns(struct filter * filter,int fcrdns)1174 filter_check_fcrdns(struct filter *filter, int fcrdns)
1175 {
1176 	int	ret = 0;
1177 
1178 	if (!filter->config->fcrdns)
1179 		return 0;
1180 
1181 	ret = fcrdns == 1;
1182 	return filter->config->not_fcrdns < 0 ? !ret : ret;
1183 }
1184 
1185 static int
filter_check_rdns(struct filter * filter,const char * hostname)1186 filter_check_rdns(struct filter *filter, const char *hostname)
1187 {
1188 	int	ret = 0;
1189 	struct netaddr	netaddr;
1190 
1191 	if (!filter->config->rdns)
1192 		return 0;
1193 
1194 	/* this is a hack until smtp session properly deals with lack of rdns */
1195 	ret = strcmp("<unknown>", hostname);
1196 	if (ret == 0)
1197 		return filter->config->not_rdns < 0 ? !ret : ret;
1198 
1199 	/* if text_to_netaddress succeeds,
1200 	 * we don't have an rDNS so the filter should match
1201 	 */
1202 	ret = !text_to_netaddr(&netaddr, hostname);
1203 	return filter->config->not_rdns < 0 ? !ret : ret;
1204 }
1205 
1206 static int
filter_builtins_notimpl(struct filter_session * fs,struct filter * filter,uint64_t reqid,const char * param)1207 filter_builtins_notimpl(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
1208 {
1209 	return 0;
1210 }
1211 
1212 static int
filter_builtins_global(struct filter_session * fs,struct filter * filter,uint64_t reqid)1213 filter_builtins_global(struct filter_session *fs, struct filter *filter, uint64_t reqid)
1214 {
1215 	return filter_check_fcrdns(filter, fs->fcrdns) ||
1216 	    filter_check_rdns(filter, fs->rdns) ||
1217 	    filter_check_rdns_table(filter, K_DOMAIN, fs->rdns) ||
1218 	    filter_check_rdns_regex(filter, fs->rdns) ||
1219 	    filter_check_src_table(filter, K_NETADDR, ss_to_text(&fs->ss_src)) ||
1220 	    filter_check_src_regex(filter, ss_to_text(&fs->ss_src)) ||
1221 	    filter_check_helo_table(filter, K_DOMAIN, fs->helo) ||
1222 	    filter_check_helo_regex(filter, fs->helo) ||
1223 	    filter_check_auth(filter, fs->username) ||
1224 	    filter_check_auth_table(filter, K_STRING, fs->username) ||
1225 	    filter_check_auth_table(filter, K_CREDENTIALS, fs->username) ||
1226 	    filter_check_auth_regex(filter, fs->username) ||
1227 	    filter_check_mail_from_table(filter, K_MAILADDR, fs->mail_from) ||
1228 	    filter_check_mail_from_regex(filter, fs->mail_from);
1229 }
1230 
1231 static int
filter_builtins_connect(struct filter_session * fs,struct filter * filter,uint64_t reqid,const char * param)1232 filter_builtins_connect(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
1233 {
1234 	return filter_builtins_global(fs, filter, reqid);
1235 }
1236 
1237 static int
filter_builtins_helo(struct filter_session * fs,struct filter * filter,uint64_t reqid,const char * param)1238 filter_builtins_helo(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
1239 {
1240 	return filter_builtins_global(fs, filter, reqid);
1241 }
1242 
1243 static int
filter_builtins_mail_from(struct filter_session * fs,struct filter * filter,uint64_t reqid,const char * param)1244 filter_builtins_mail_from(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
1245 {
1246 	return filter_builtins_global(fs, filter, reqid);
1247 }
1248 
1249 static int
filter_builtins_rcpt_to(struct filter_session * fs,struct filter * filter,uint64_t reqid,const char * param)1250 filter_builtins_rcpt_to(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
1251 {
1252 	return filter_builtins_global(fs, filter, reqid) ||
1253 	    filter_check_rcpt_to_table(filter, K_MAILADDR, param) ||
1254 	    filter_check_rcpt_to_regex(filter, param);
1255 }
1256 
1257 static int
filter_builtins_data(struct filter_session * fs,struct filter * filter,uint64_t reqid,const char * param)1258 filter_builtins_data(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
1259 {
1260 	return filter_builtins_global(fs, filter, reqid);
1261 }
1262 
1263 static int
filter_builtins_commit(struct filter_session * fs,struct filter * filter,uint64_t reqid,const char * param)1264 filter_builtins_commit(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
1265 {
1266 	return filter_builtins_global(fs, filter, reqid);
1267 }
1268 
1269 static void
1270 report_smtp_broadcast(uint64_t, const char *, struct timeval *, const char *,
1271     const char *, ...) __attribute__((__format__ (printf, 5, 6)));
1272 
1273 void
lka_report_init(void)1274 lka_report_init(void)
1275 {
1276 	struct reporters	*tailq;
1277 	size_t			 i;
1278 
1279 	dict_init(&report_smtp_in);
1280 	dict_init(&report_smtp_out);
1281 
1282 	for (i = 0; i < nitems(smtp_events); ++i) {
1283 		tailq = xcalloc(1, sizeof (struct reporters));
1284 		TAILQ_INIT(tailq);
1285 		dict_xset(&report_smtp_in, smtp_events[i].event, tailq);
1286 
1287 		tailq = xcalloc(1, sizeof (struct reporters));
1288 		TAILQ_INIT(tailq);
1289 		dict_xset(&report_smtp_out, smtp_events[i].event, tailq);
1290 	}
1291 }
1292 
1293 void
lka_report_register_hook(const char * name,const char * hook)1294 lka_report_register_hook(const char *name, const char *hook)
1295 {
1296 	struct dict	*subsystem;
1297 	struct reporter_proc	*rp;
1298 	struct reporters	*tailq;
1299 	void *iter;
1300 	size_t	i;
1301 
1302 	if (strncmp(hook, "smtp-in|", 8) == 0) {
1303 		subsystem = &report_smtp_in;
1304 		hook += 8;
1305 	}
1306 	else if (strncmp(hook, "smtp-out|", 9) == 0) {
1307 		subsystem = &report_smtp_out;
1308 		hook += 9;
1309 	}
1310 	else
1311 		fatalx("Invalid message direction: %s", hook);
1312 
1313 	if (strcmp(hook, "*") == 0) {
1314 		iter = NULL;
1315 		while (dict_iter(subsystem, &iter, NULL, (void **)&tailq)) {
1316 			rp = xcalloc(1, sizeof *rp);
1317 			rp->name = xstrdup(name);
1318 			TAILQ_INSERT_TAIL(tailq, rp, entries);
1319 		}
1320 		return;
1321 	}
1322 
1323 	for (i = 0; i < nitems(smtp_events); i++)
1324 		if (strcmp(hook, smtp_events[i].event) == 0)
1325 			break;
1326 	if (i == nitems(smtp_events))
1327 		fatalx("Unrecognized report name: %s", hook);
1328 
1329 	tailq = dict_get(subsystem, hook);
1330 	rp = xcalloc(1, sizeof *rp);
1331 	rp->name = xstrdup(name);
1332 	TAILQ_INSERT_TAIL(tailq, rp, entries);
1333 }
1334 
1335 static void
report_smtp_broadcast(uint64_t reqid,const char * direction,struct timeval * tv,const char * event,const char * format,...)1336 report_smtp_broadcast(uint64_t reqid, const char *direction, struct timeval *tv, const char *event,
1337     const char *format, ...)
1338 {
1339 	va_list		ap;
1340 	struct dict	*d;
1341 	struct reporters	*tailq;
1342 	struct reporter_proc	*rp;
1343 
1344 	if (strcmp("smtp-in", direction) == 0)
1345 		d = &report_smtp_in;
1346 
1347 	else if (strcmp("smtp-out", direction) == 0)
1348 		d = &report_smtp_out;
1349 
1350 	else
1351 		fatalx("unexpected direction: %s", direction);
1352 
1353 	tailq = dict_xget(d, event);
1354 	TAILQ_FOREACH(rp, tailq, entries) {
1355 		if (!lka_filter_proc_in_session(reqid, rp->name))
1356 			continue;
1357 
1358 		va_start(ap, format);
1359 		if (io_printf(lka_proc_get_io(rp->name),
1360 		    "report|%s|%lld.%06ld|%s|%s|%016"PRIx64"%s",
1361 		    PROTOCOL_VERSION, (long long)tv->tv_sec, (long)tv->tv_usec,
1362 		    direction, event, reqid,
1363 		    format[0] != '\n' ? "|" : "") == -1 ||
1364 		    io_vprintf(lka_proc_get_io(rp->name), format, ap) == -1)
1365 			fatalx("failed to write to processor");
1366 		va_end(ap);
1367 	}
1368 }
1369 
1370 void
lka_report_smtp_link_connect(const char * direction,struct timeval * tv,uint64_t reqid,const char * rdns,int fcrdns,const struct sockaddr_storage * ss_src,const struct sockaddr_storage * ss_dest)1371 lka_report_smtp_link_connect(const char *direction, struct timeval *tv, uint64_t reqid, const char *rdns,
1372     int fcrdns,
1373     const struct sockaddr_storage *ss_src,
1374     const struct sockaddr_storage *ss_dest)
1375 {
1376 	struct filter_session *fs;
1377 	char	src[NI_MAXHOST + 5];
1378 	char	dest[NI_MAXHOST + 5];
1379 	uint16_t	src_port = 0;
1380 	uint16_t	dest_port = 0;
1381 	const char     *fcrdns_str;
1382 
1383 	if (ss_src->ss_family == AF_INET)
1384 		src_port = ntohs(((const struct sockaddr_in *)ss_src)->sin_port);
1385 	else if (ss_src->ss_family == AF_INET6)
1386 		src_port = ntohs(((const struct sockaddr_in6 *)ss_src)->sin6_port);
1387 
1388 	if (ss_dest->ss_family == AF_INET)
1389 		dest_port = ntohs(((const struct sockaddr_in *)ss_dest)->sin_port);
1390 	else if (ss_dest->ss_family == AF_INET6)
1391 		dest_port = ntohs(((const struct sockaddr_in6 *)ss_dest)->sin6_port);
1392 
1393 	if (strcmp(ss_to_text(ss_src), "local") == 0) {
1394 		(void)snprintf(src, sizeof src, "unix:%s", SMTPD_SOCKET);
1395 		(void)snprintf(dest, sizeof dest, "unix:%s", SMTPD_SOCKET);
1396 	} else {
1397 		(void)snprintf(src, sizeof src, "%s:%d", ss_to_text(ss_src), src_port);
1398 		(void)snprintf(dest, sizeof dest, "%s:%d", ss_to_text(ss_dest), dest_port);
1399 	}
1400 
1401 	switch (fcrdns) {
1402 	case 1:
1403 		fcrdns_str = "pass";
1404 		break;
1405 	case 0:
1406 		fcrdns_str = "fail";
1407 		break;
1408 	default:
1409 		fcrdns_str = "error";
1410 		break;
1411 	}
1412 
1413 	fs = tree_xget(&sessions, reqid);
1414 	fs->rdns = xstrdup(rdns);
1415 	fs->fcrdns = fcrdns;
1416 	fs->ss_src = *ss_src;
1417 	fs->ss_dest = *ss_dest;
1418 
1419 	report_smtp_broadcast(reqid, direction, tv, "link-connect",
1420 	    "%s|%s|%s|%s\n", rdns, fcrdns_str, src, dest);
1421 }
1422 
1423 void
lka_report_smtp_link_disconnect(const char * direction,struct timeval * tv,uint64_t reqid)1424 lka_report_smtp_link_disconnect(const char *direction, struct timeval *tv, uint64_t reqid)
1425 {
1426 	report_smtp_broadcast(reqid, direction, tv, "link-disconnect", "\n");
1427 }
1428 
1429 void
lka_report_smtp_link_greeting(const char * direction,uint64_t reqid,struct timeval * tv,const char * domain)1430 lka_report_smtp_link_greeting(const char *direction, uint64_t reqid,
1431     struct timeval *tv, const char *domain)
1432 {
1433 	report_smtp_broadcast(reqid, direction, tv, "link-greeting", "%s\n",
1434 	    domain);
1435 }
1436 
1437 void
lka_report_smtp_link_auth(const char * direction,struct timeval * tv,uint64_t reqid,const char * username,const char * result)1438 lka_report_smtp_link_auth(const char *direction, struct timeval *tv, uint64_t reqid,
1439     const char *username, const char *result)
1440 {
1441 	struct filter_session *fs;
1442 
1443 	if (strcmp(result, "pass") == 0) {
1444 		fs = tree_xget(&sessions, reqid);
1445 		fs->username = xstrdup(username);
1446 	}
1447 	report_smtp_broadcast(reqid, direction, tv, "link-auth", "%s|%s\n",
1448 	    result, username);
1449 }
1450 
1451 void
lka_report_smtp_link_identify(const char * direction,struct timeval * tv,uint64_t reqid,const char * method,const char * heloname)1452 lka_report_smtp_link_identify(const char *direction, struct timeval *tv,
1453     uint64_t reqid, const char *method, const char *heloname)
1454 {
1455 	report_smtp_broadcast(reqid, direction, tv, "link-identify", "%s|%s\n",
1456 	    method, heloname);
1457 }
1458 
1459 void
lka_report_smtp_link_tls(const char * direction,struct timeval * tv,uint64_t reqid,const char * ciphers)1460 lka_report_smtp_link_tls(const char *direction, struct timeval *tv, uint64_t reqid, const char *ciphers)
1461 {
1462 	report_smtp_broadcast(reqid, direction, tv, "link-tls", "%s\n",
1463 	    ciphers);
1464 }
1465 
1466 void
lka_report_smtp_tx_reset(const char * direction,struct timeval * tv,uint64_t reqid,uint32_t msgid)1467 lka_report_smtp_tx_reset(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
1468 {
1469 	report_smtp_broadcast(reqid, direction, tv, "tx-reset", "%08x\n",
1470 	    msgid);
1471 }
1472 
1473 void
lka_report_smtp_tx_begin(const char * direction,struct timeval * tv,uint64_t reqid,uint32_t msgid)1474 lka_report_smtp_tx_begin(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
1475 {
1476 	report_smtp_broadcast(reqid, direction, tv, "tx-begin", "%08x\n",
1477 	    msgid);
1478 }
1479 
1480 void
lka_report_smtp_tx_mail(const char * direction,struct timeval * tv,uint64_t reqid,uint32_t msgid,const char * address,int ok)1481 lka_report_smtp_tx_mail(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok)
1482 {
1483 	const char *result;
1484 
1485 	switch (ok) {
1486 	case 1:
1487 		result = "ok";
1488 		break;
1489 	case 0:
1490 		result = "permfail";
1491 		break;
1492 	default:
1493 		result = "tempfail";
1494 		break;
1495 	}
1496 	report_smtp_broadcast(reqid, direction, tv, "tx-mail", "%08x|%s|%s\n",
1497 	    msgid, result, address);
1498 }
1499 
1500 void
lka_report_smtp_tx_rcpt(const char * direction,struct timeval * tv,uint64_t reqid,uint32_t msgid,const char * address,int ok)1501 lka_report_smtp_tx_rcpt(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok)
1502 {
1503 	const char *result;
1504 
1505 	switch (ok) {
1506 	case 1:
1507 		result = "ok";
1508 		break;
1509 	case 0:
1510 		result = "permfail";
1511 		break;
1512 	default:
1513 		result = "tempfail";
1514 		break;
1515 	}
1516 	report_smtp_broadcast(reqid, direction, tv, "tx-rcpt", "%08x|%s|%s\n",
1517 	    msgid, result, address);
1518 }
1519 
1520 void
lka_report_smtp_tx_envelope(const char * direction,struct timeval * tv,uint64_t reqid,uint32_t msgid,uint64_t evpid)1521 lka_report_smtp_tx_envelope(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, uint64_t evpid)
1522 {
1523 	report_smtp_broadcast(reqid, direction, tv, "tx-envelope",
1524 	    "%08x|%016"PRIx64"\n", msgid, evpid);
1525 }
1526 
1527 void
lka_report_smtp_tx_data(const char * direction,struct timeval * tv,uint64_t reqid,uint32_t msgid,int ok)1528 lka_report_smtp_tx_data(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, int ok)
1529 {
1530 	const char *result;
1531 
1532 	switch (ok) {
1533 	case 1:
1534 		result = "ok";
1535 		break;
1536 	case 0:
1537 		result = "permfail";
1538 		break;
1539 	default:
1540 		result = "tempfail";
1541 		break;
1542 	}
1543 	report_smtp_broadcast(reqid, direction, tv, "tx-data", "%08x|%s\n",
1544 	    msgid, result);
1545 }
1546 
1547 void
lka_report_smtp_tx_commit(const char * direction,struct timeval * tv,uint64_t reqid,uint32_t msgid,size_t msgsz)1548 lka_report_smtp_tx_commit(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, size_t msgsz)
1549 {
1550 	report_smtp_broadcast(reqid, direction, tv, "tx-commit", "%08x|%zd\n",
1551 	    msgid, msgsz);
1552 }
1553 
1554 void
lka_report_smtp_tx_rollback(const char * direction,struct timeval * tv,uint64_t reqid,uint32_t msgid)1555 lka_report_smtp_tx_rollback(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
1556 {
1557 	report_smtp_broadcast(reqid, direction, tv, "tx-rollback", "%08x\n",
1558 	    msgid);
1559 }
1560 
1561 void
lka_report_smtp_protocol_client(const char * direction,struct timeval * tv,uint64_t reqid,const char * command)1562 lka_report_smtp_protocol_client(const char *direction, struct timeval *tv, uint64_t reqid, const char *command)
1563 {
1564 	report_smtp_broadcast(reqid, direction, tv, "protocol-client", "%s\n",
1565 	    command);
1566 }
1567 
1568 void
lka_report_smtp_protocol_server(const char * direction,struct timeval * tv,uint64_t reqid,const char * response)1569 lka_report_smtp_protocol_server(const char *direction, struct timeval *tv, uint64_t reqid, const char *response)
1570 {
1571 	report_smtp_broadcast(reqid, direction, tv, "protocol-server", "%s\n",
1572 	    response);
1573 }
1574 
1575 void
lka_report_smtp_filter_response(const char * direction,struct timeval * tv,uint64_t reqid,int phase,int response,const char * param)1576 lka_report_smtp_filter_response(const char *direction, struct timeval *tv, uint64_t reqid,
1577     int phase, int response, const char *param)
1578 {
1579 	const char *phase_name;
1580 	const char *response_name;
1581 
1582 	switch (phase) {
1583 	case FILTER_CONNECT:
1584 		phase_name = "connected";
1585 		break;
1586 	case FILTER_HELO:
1587 		phase_name = "helo";
1588 		break;
1589 	case FILTER_EHLO:
1590 		phase_name = "ehlo";
1591 		break;
1592 	case FILTER_STARTTLS:
1593 		phase_name = "tls";
1594 		break;
1595 	case FILTER_AUTH:
1596 		phase_name = "auth";
1597 		break;
1598 	case FILTER_MAIL_FROM:
1599 		phase_name = "mail-from";
1600 		break;
1601 	case FILTER_RCPT_TO:
1602 		phase_name = "rcpt-to";
1603 		break;
1604 	case FILTER_DATA:
1605 		phase_name = "data";
1606 		break;
1607 	case FILTER_DATA_LINE:
1608 		phase_name = "data-line";
1609 		break;
1610 	case FILTER_RSET:
1611 		phase_name = "rset";
1612 		break;
1613 	case FILTER_QUIT:
1614 		phase_name = "quit";
1615 		break;
1616 	case FILTER_NOOP:
1617 		phase_name = "noop";
1618 		break;
1619 	case FILTER_HELP:
1620 		phase_name = "help";
1621 		break;
1622 	case FILTER_WIZ:
1623 		phase_name = "wiz";
1624 		break;
1625 	case FILTER_COMMIT:
1626 		phase_name = "commit";
1627 		break;
1628 	default:
1629 		phase_name = "";
1630 	}
1631 
1632 	switch (response) {
1633 	case FILTER_PROCEED:
1634 		response_name = "proceed";
1635 		break;
1636 	case FILTER_JUNK:
1637 		response_name = "junk";
1638 		break;
1639 	case FILTER_REWRITE:
1640 		response_name = "rewrite";
1641 		break;
1642 	case FILTER_REJECT:
1643 		response_name = "reject";
1644 		break;
1645 	case FILTER_DISCONNECT:
1646 		response_name = "disconnect";
1647 		break;
1648 	default:
1649 		response_name = "";
1650 	}
1651 
1652 	report_smtp_broadcast(reqid, direction, tv, "filter-response",
1653 	    "%s|%s%s%s\n", phase_name, response_name, param ? "|" : "",
1654 	    param ? param : "");
1655 }
1656 
1657 void
lka_report_smtp_timeout(const char * direction,struct timeval * tv,uint64_t reqid)1658 lka_report_smtp_timeout(const char *direction, struct timeval *tv, uint64_t reqid)
1659 {
1660 	report_smtp_broadcast(reqid, direction, tv, "timeout", "\n");
1661 }
1662 
1663 void
lka_report_filter_report(uint64_t reqid,const char * name,int builtin,const char * direction,struct timeval * tv,const char * message)1664 lka_report_filter_report(uint64_t reqid, const char *name, int builtin,
1665     const char *direction, struct timeval *tv, const char *message)
1666 {
1667 	report_smtp_broadcast(reqid, direction, tv, "filter-report",
1668 	    "%s|%s|%s\n", builtin ? "builtin" : "proc",
1669 	    name, message);
1670 }
1671 
1672 void
lka_report_proc(const char * name,const char * line)1673 lka_report_proc(const char *name, const char *line)
1674 {
1675 	char buffer[LINE_MAX];
1676 	struct timeval tv;
1677 	char *ep, *sp, *direction;
1678 	uint64_t reqid;
1679 
1680 	if (strlcpy(buffer, line + 7, sizeof(buffer)) >= sizeof(buffer))
1681 		fatalx("Invalid report: line too long: %s", line);
1682 
1683 	errno = 0;
1684 	tv.tv_sec = strtoll(buffer, &ep, 10);
1685 	if (ep[0] != '.' || errno != 0)
1686 		fatalx("Invalid report: invalid time: %s", line);
1687 	sp = ep + 1;
1688 	tv.tv_usec = strtol(sp, &ep, 10);
1689 	if (ep[0] != '|' || errno != 0)
1690 		fatalx("Invalid report: invalid time: %s", line);
1691 	if (ep - sp != 6)
1692 		fatalx("Invalid report: invalid time: %s", line);
1693 
1694 	direction = ep + 1;
1695 	if (strncmp(direction, "smtp-in|", 8) == 0) {
1696 		direction[7] = '\0';
1697 		direction += 7;
1698 #if 0
1699 	} else if (strncmp(direction, "smtp-out|", 9) == 0) {
1700 		direction[8] = '\0';
1701 		direction += 8;
1702 #endif
1703 	} else
1704 		fatalx("Invalid report: invalid direction: %s", line);
1705 
1706 	reqid = strtoull(sp, &ep, 16);
1707 	if (ep[0] != '|' || errno != 0)
1708 		fatalx("Invalid report: invalid reqid: %s", line);
1709 	sp = ep + 1;
1710 
1711 	lka_report_filter_report(reqid, name, 0, direction, &tv, sp);
1712 }
1713