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