summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorGilles Chehade <gilles@cvs.openbsd.org>2018-12-21 14:33:53 +0000
committerGilles Chehade <gilles@cvs.openbsd.org>2018-12-21 14:33:53 +0000
commit094f3a27e7039a788aa328ab2800d14d8d0ba591 (patch)
treefbb0ba82b3e60515aa4ecf5265944d0c9502c704 /usr.sbin
parent1b811f60c0e491dd91800a159c62f7337d9b13b3 (diff)
bring in new grammar for filters, allowing filter chains and plugging of
different filters & chains on different interfaces. in this diff, proc filters are still disabled as they're missing on very important piece of logic. ok eric@
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/smtpd/config.c16
-rw-r--r--usr.sbin/smtpd/lka.c40
-rw-r--r--usr.sbin/smtpd/lka_filter.c524
-rw-r--r--usr.sbin/smtpd/lka_proc.c44
-rw-r--r--usr.sbin/smtpd/lka_report.c184
-rw-r--r--usr.sbin/smtpd/parse.y207
-rw-r--r--usr.sbin/smtpd/smtp_session.c44
-rw-r--r--usr.sbin/smtpd/smtpd.h33
8 files changed, 835 insertions, 257 deletions
diff --git a/usr.sbin/smtpd/config.c b/usr.sbin/smtpd/config.c
index 22f367190a8..552803cbcee 100644
--- a/usr.sbin/smtpd/config.c
+++ b/usr.sbin/smtpd/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.46 2018/11/30 15:33:40 gilles Exp $ */
+/* $OpenBSD: config.c,v 1.47 2018/12/21 14:33:52 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -48,7 +48,6 @@ config_default(void)
struct mta_limits *limits = NULL;
struct table *t = NULL;
char hostname[HOST_NAME_MAX+1];
- uint8_t i;
if (getmailname(hostname, sizeof hostname) == -1)
return NULL;
@@ -90,9 +89,8 @@ config_default(void)
conf->sc_limits_dict = calloc(1, sizeof(*conf->sc_limits_dict));
conf->sc_mda_wrappers = calloc(1, sizeof(*conf->sc_mda_wrappers));
conf->sc_processors_dict = calloc(1, sizeof(*conf->sc_processors_dict));
- conf->sc_smtp_reporters_dict = calloc(1, sizeof(*conf->sc_smtp_reporters_dict));
- conf->sc_mta_reporters_dict = calloc(1, sizeof(*conf->sc_mta_reporters_dict));
conf->sc_dispatcher_bounce = calloc(1, sizeof(*conf->sc_dispatcher_bounce));
+ conf->sc_filters_dict = calloc(1, sizeof(*conf->sc_filters_dict));
limits = calloc(1, sizeof(*limits));
if (conf->sc_tables_dict == NULL ||
@@ -105,9 +103,8 @@ config_default(void)
conf->sc_limits_dict == NULL ||
conf->sc_mda_wrappers == NULL ||
conf->sc_processors_dict == NULL ||
- conf->sc_smtp_reporters_dict == NULL||
- conf->sc_mta_reporters_dict == NULL ||
conf->sc_dispatcher_bounce == NULL ||
+ conf->sc_filters_dict == NULL ||
limits == NULL)
goto error;
@@ -119,8 +116,6 @@ config_default(void)
dict_init(conf->sc_tables_dict);
dict_init(conf->sc_limits_dict);
dict_init(conf->sc_processors_dict);
- dict_init(conf->sc_smtp_reporters_dict);
- dict_init(conf->sc_mta_reporters_dict);
limit_mta_set_defaults(limits);
@@ -129,8 +124,6 @@ config_default(void)
TAILQ_INIT(conf->sc_listeners);
TAILQ_INIT(conf->sc_rules);
- for (i = 0; i < nitems(conf->sc_filter_rules); ++i)
- TAILQ_INIT(&conf->sc_filter_rules[i]);
/* bounce dispatcher */
conf->sc_dispatcher_bounce->type = DISPATCHER_BOUNCE;
@@ -164,8 +157,7 @@ error:
free(conf->sc_mda_wrappers);
free(conf->sc_processors_dict);
free(conf->sc_dispatcher_bounce);
- free(conf->sc_smtp_reporters_dict);
- free(conf->sc_mta_reporters_dict);
+ free(conf->sc_filters_dict);
free(limits);
free(conf);
return NULL;
diff --git a/usr.sbin/smtpd/lka.c b/usr.sbin/smtpd/lka.c
index 2fc7a6034f8..27569d1978a 100644
--- a/usr.sbin/smtpd/lka.c
+++ b/usr.sbin/smtpd/lka.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka.c,v 1.227 2018/12/13 17:08:10 gilles Exp $ */
+/* $OpenBSD: lka.c,v 1.228 2018/12/21 14:33:52 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -60,6 +60,10 @@ static int lka_X509_verify(struct ca_vrfy_req_msg *, const char *, const char *)
static void lka_certificate_verify(enum imsg_type, struct ca_vrfy_req_msg *);
static void lka_certificate_verify_resume(enum imsg_type, struct ca_vrfy_req_msg *);
+static void proc_timeout(int fd, short event, void *p);
+
+struct event ev_proc_ready;
+
static void
lka_imsg(struct mproc *p, struct imsg *imsg)
{
@@ -89,6 +93,7 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
const char *ciphers;
const char *address;
const char *heloname;
+ const char *filter_name;
struct sockaddr_storage ss_src, ss_dest;
int filter_response;
int filter_phase;
@@ -371,8 +376,11 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
NULL) == -1)
err(1, "pledge");
- /* Start fulfilling requests */
- mproc_enable(p_pony);
+ /* setup proc registering task */
+ evtimer_set(&ev_proc_ready, proc_timeout, &ev_proc_ready);
+ tv.tv_sec = 0;
+ tv.tv_usec = 10;
+ evtimer_add(&ev_proc_ready, &tv);
return;
case IMSG_LKA_OPEN_FORWARD:
@@ -601,13 +609,14 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
case IMSG_FILTER_SMTP_BEGIN:
m_msg(&m, imsg);
m_get_id(&m, &reqid);
+ m_get_string(&m, &filter_name);
m_get_sockaddr(&m, (struct sockaddr *)&ss_src);
m_get_sockaddr(&m, (struct sockaddr *)&ss_dest);
m_get_string(&m, &rdns);
m_get_int(&m, &fcrdns);
m_end(&m);
- lka_filter_begin(reqid, &ss_src, &ss_dest, rdns, fcrdns);
+ lka_filter_begin(reqid, filter_name, &ss_src, &ss_dest, rdns, fcrdns);
return;
case IMSG_FILTER_SMTP_END:
@@ -699,6 +708,9 @@ lka(void)
/* Ignore them until we get our config */
mproc_disable(p_pony);
+ lka_report_init();
+ lka_filter_init();
+
/* proc & exec will be revoked before serving requests */
if (pledge("stdio rpath inet dns getpw recvfd sendfd proc exec", NULL) == -1)
err(1, "pledge");
@@ -709,6 +721,26 @@ lka(void)
return (0);
}
+static void
+proc_timeout(int fd, short event, void *p)
+{
+ struct event *ev = p;
+ struct timeval tv;
+
+ if (!lka_proc_ready())
+ goto reset;
+
+ lka_filter_ready();
+ mproc_enable(p_pony);
+ return;
+
+reset:
+ tv.tv_sec = 0;
+ tv.tv_usec = 10;
+ evtimer_add(ev, &tv);
+}
+
+
static int
lka_authenticate(const char *tablename, const char *user, const char *password)
{
diff --git a/usr.sbin/smtpd/lka_filter.c b/usr.sbin/smtpd/lka_filter.c
index b7b0a5b6418..7a5b9fa7fbe 100644
--- a/usr.sbin/smtpd/lka_filter.c
+++ b/usr.sbin/smtpd/lka_filter.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka_filter.c,v 1.15 2018/12/14 20:22:52 gilles Exp $ */
+/* $OpenBSD: lka_filter.c,v 1.16 2018/12/21 14:33:52 gilles Exp $ */
/*
* Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
@@ -35,62 +35,243 @@
#include "smtpd.h"
#include "log.h"
-static void filter_proceed(uint64_t);
-static void filter_rewrite(uint64_t, const char *);
-static void filter_reject(uint64_t, const char *);
-static void filter_disconnect(uint64_t, const char *);
+struct filter;
-static void filter_data(uint64_t reqid, const char *line);
+static void filter_protocol(uint64_t, enum filter_phase, const char *);
+static void filter_protocol_next(uint64_t, uint64_t, const char *);
+static void filter_protocol_query(struct filter *, uint64_t, uint64_t, const char *, const char *);
-static void filter_write(const char *, uint64_t, const char *, const char *);
-static void filter_write_dataline(const char *, uint64_t, const char *);
+static void filter_data(uint64_t, const char *);
+static void filter_data_next(uint64_t, uint64_t, const char *);
+static void filter_data_query(struct filter *, uint64_t, uint64_t, const char *);
-static int filter_exec_notimpl(uint64_t, struct filter_rule *, const char *);
-static int filter_exec_connected(uint64_t, struct filter_rule *, const char *);
-static int filter_exec_helo(uint64_t, struct filter_rule *, const char *);
-static int filter_exec_mail_from(uint64_t, struct filter_rule *, const char *);
-static int filter_exec_rcpt_to(uint64_t, struct filter_rule *, const char *);
+static int filter_builtins_notimpl(struct filter *, uint64_t, const char *);
+static int filter_builtins_connect(struct filter *, uint64_t, const char *);
+static int filter_builtins_helo(struct filter *, uint64_t, const char *);
+static int filter_builtins_mail_from(struct filter *, uint64_t, const char *);
+static int filter_builtins_rcpt_to(struct filter *, uint64_t, const char *);
+
+static void filter_result_proceed(uint64_t);
+static void filter_result_rewrite(uint64_t, const char *);
+static void filter_result_reject(uint64_t, const char *);
+static void filter_result_disconnect(uint64_t, const char *);
static void filter_session_io(struct io *, int, void *);
int lka_filter_process_response(const char *, const char *);
-static void filter_data_next(uint64_t, const char *, const char *);
+
#define PROTOCOL_VERSION 1
static struct filter_exec {
enum filter_phase phase;
const char *phase_name;
- int (*func)(uint64_t, struct filter_rule *, const char *);
-} filter_execs[] = {
- { FILTER_AUTH, "auth", filter_exec_notimpl },
- { FILTER_CONNECTED, "connected", filter_exec_connected },
- { FILTER_DATA, "data", filter_exec_notimpl },
- { FILTER_EHLO, "ehlo", filter_exec_helo },
- { FILTER_HELO, "helo", filter_exec_helo },
- { FILTER_STARTTLS, "starttls", filter_exec_notimpl },
- { FILTER_MAIL_FROM, "mail-from", filter_exec_mail_from },
- { FILTER_NOOP, "noop", filter_exec_notimpl },
- { FILTER_QUIT, "quit", filter_exec_notimpl },
- { FILTER_RCPT_TO, "rcpt-to", filter_exec_rcpt_to },
- { FILTER_RSET, "rset", filter_exec_notimpl },
- { FILTER_COMMIT, "commit", filter_exec_notimpl },
+ int (*func)(struct filter *, uint64_t, const char *);
+} filter_execs[FILTER_PHASES_COUNT] = {
+ { FILTER_CONNECT, "connect", filter_builtins_connect },
+ { FILTER_HELO, "helo", filter_builtins_helo },
+ { FILTER_EHLO, "ehlo", filter_builtins_helo },
+ { FILTER_STARTTLS, "starttls", filter_builtins_notimpl },
+ { FILTER_AUTH, "auth", filter_builtins_notimpl },
+ { FILTER_MAIL_FROM, "mail-from", filter_builtins_mail_from },
+ { FILTER_RCPT_TO, "rcpt-to", filter_builtins_rcpt_to },
+ { FILTER_DATA, "data", filter_builtins_notimpl },
+ { FILTER_DATA_LINE, "data-line", filter_builtins_notimpl },
+ { FILTER_RSET, "rset", filter_builtins_notimpl },
+ { FILTER_QUIT, "quit", filter_builtins_notimpl },
+ { FILTER_NOOP, "noop", filter_builtins_notimpl },
+ { FILTER_HELP, "help", filter_builtins_notimpl },
+ { FILTER_WIZ, "wiz", filter_builtins_notimpl },
+ { FILTER_COMMIT, "commit", filter_builtins_notimpl },
};
-static struct tree sessions;
-static int inited;
-
struct filter_session {
uint64_t id;
struct io *io;
+ char *filter_name;
struct sockaddr_storage ss_src;
struct sockaddr_storage ss_dest;
char *rdns;
int fcrdns;
+
+ enum filter_phase phase;
};
+struct filter {
+ uint64_t id;
+ uint32_t phases;
+ const char *name;
+ const char *proc;
+ struct filter **chain;
+ size_t chain_size;
+ struct filter_config *config;
+};
+static struct dict filters;
+
+struct filter_entry {
+ TAILQ_ENTRY(filter_entry) entries;
+ uint64_t id;
+ const char *name;
+};
+
+struct filter_chain {
+ TAILQ_HEAD(, filter_entry) chain[nitems(filter_execs)];
+};
+
+static struct dict smtp_in;
+
+static struct tree sessions;
+static int inited;
+
+static struct dict filter_chains;
+
+
+void
+lka_filter_init(void)
+{
+ void *iter;
+ const char *name;
+ struct filter *filter;
+ struct filter_config *filter_config;
+ size_t i;
+
+ dict_init(&filters);
+ dict_init(&filter_chains);
+
+ iter = NULL;
+ while (dict_iter(env->sc_filters_dict, &iter, &name, (void **)&filter_config)) {
+ switch (filter_config->filter_type) {
+ case FILTER_TYPE_BUILTIN:
+ filter = xcalloc(1, sizeof(*filter));
+ filter->name = name;
+ filter->phases |= (1<<filter_config->phase);
+ filter->config = filter_config;
+ dict_set(&filters, name, filter);
+ break;
+
+ case FILTER_TYPE_PROC:
+ filter = xcalloc(1, sizeof(*filter));
+ filter->name = name;
+ filter->proc = filter_config->proc;
+ filter->config = filter_config;
+ dict_set(&filters, name, filter);
+ break;
+
+ case FILTER_TYPE_CHAIN:
+ filter = xcalloc(1, sizeof(*filter));
+ filter->name = name;
+ filter->chain = xcalloc(filter_config->chain_size, sizeof(void **));
+ filter->chain_size = filter_config->chain_size;
+ filter->config = filter_config;
+ for (i = 0; i < filter->chain_size; ++i)
+ filter->chain[i] = dict_get(&filters, filter_config->chain[i]);
+ dict_set(&filters, name, filter);
+ break;
+ }
+ }
+}
+
+void
+lka_filter_register_hook(const char *name, const char *hook)
+{
+ struct dict *subsystem;
+ struct filter *filter;
+ const char *filter_name;
+ void *iter;
+ size_t i;
+
+ if (strncasecmp(hook, "smtp-in|", 8) == 0) {
+ subsystem = &smtp_in;
+ hook += 8;
+ }
+ else
+ return;
+
+ for (i = 0; i < nitems(filter_execs); i++)
+ if (strcmp(hook, filter_execs[i].phase_name) == 0)
+ break;
+ if (i == nitems(filter_execs))
+ return;
+
+ iter = NULL;
+ while (dict_iter(&filters, &iter, &filter_name, (void **)&filter))
+ if (filter->proc && strcmp(name, filter->proc) == 0)
+ filter->phases |= (1<<filter_execs[i].phase);
+}
+
+void
+lka_filter_ready(void)
+{
+ struct filter *filter;
+ struct filter *subfilter;
+ const char *filter_name;
+ struct filter_entry *filter_entry;
+ struct filter_chain *filter_chain;
+ void *iter;
+ size_t i;
+ size_t j;
+
+ iter = NULL;
+ while (dict_iter(&filters, &iter, &filter_name, (void **)&filter)) {
+ filter_chain = xcalloc(1, sizeof *filter_chain);
+ for (i = 0; i < nitems(filter_execs); i++)
+ TAILQ_INIT(&filter_chain->chain[i]);
+ dict_set(&filter_chains, filter_name, filter_chain);
+
+ if (filter->chain) {
+ for (i = 0; i < filter->chain_size; i++) {
+ subfilter = filter->chain[i];
+ for (j = 0; j < nitems(filter_execs); ++j) {
+ if (subfilter->phases & (1<<j)) {
+ filter_entry = xcalloc(1, sizeof *filter_entry);
+ filter_entry->id = generate_uid();
+ filter_entry->name = subfilter->name;
+ TAILQ_INSERT_TAIL(&filter_chain->chain[j],
+ filter_entry, entries);
+ }
+ }
+ }
+ continue;
+ }
+ for (i = 0; i < nitems(filter_execs); ++i) {
+ if (filter->phases & (1<<i)) {
+ filter_entry = xcalloc(1, sizeof *filter_entry);
+ filter_entry->id = generate_uid();
+ filter_entry->name = filter_name;
+ TAILQ_INSERT_TAIL(&filter_chain->chain[i],
+ filter_entry, entries);
+ }
+ }
+ }
+}
+
+int
+lka_filter_proc_in_session(uint64_t reqid, const char *proc)
+{
+ struct filter_session *fs;
+ struct filter *filter;
+ size_t i;
+
+ fs = tree_xget(&sessions, reqid);
+ filter = dict_get(&filters, fs->filter_name);
+
+ if (filter->proc == NULL && filter->chain == NULL)
+ return 0;
+
+ if (filter->proc)
+ return strcmp(filter->proc, proc) == 0 ? 1 : 0;
+
+ for (i = 0; i < filter->chain_size; i++)
+ if (filter->chain[i]->proc &&
+ strcmp(filter->chain[i]->proc, proc) == 0)
+ return 1;
+
+ return 0;
+}
+
void
lka_filter_begin(uint64_t reqid,
+ const char *filter_name,
const struct sockaddr_storage *ss_src,
const struct sockaddr_storage *ss_dest,
const char *rdns,
@@ -105,6 +286,7 @@ lka_filter_begin(uint64_t reqid,
fs = xcalloc(1, sizeof (struct filter_session));
fs->id = reqid;
+ fs->filter_name = xstrdup(filter_name);
fs->ss_src = *ss_src;
fs->ss_dest = *ss_dest;
fs->rdns = xstrdup(rdns);
@@ -133,7 +315,8 @@ lka_filter_data_begin(uint64_t reqid)
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1)
goto end;
-
+ io_set_nonblocking(sp[0]);
+ io_set_nonblocking(sp[1]);
fd = sp[0];
fs->io = io_new();
io_set_fd(fs->io, sp[1]);
@@ -152,8 +335,10 @@ lka_filter_data_end(uint64_t reqid)
struct filter_session *fs;
fs = tree_xget(&sessions, reqid);
- io_free(fs->io);
- fs->io = NULL;
+ if (fs->io) {
+ io_free(fs->io);
+ fs->io = NULL;
+ }
}
static void
@@ -177,6 +362,11 @@ filter_session_io(struct io *io, int evt, void *arg)
filter_data(fs->id, line);
goto nextline;
+
+ case IO_DISCONNECTED:
+ io_free(fs->io);
+ fs->io = NULL;
+ break;
}
}
@@ -184,6 +374,7 @@ int
lka_filter_process_response(const char *name, const char *line)
{
uint64_t reqid;
+ uint64_t token;
char buffer[LINE_MAX];
char *ep = NULL;
char *kind = NULL;
@@ -197,7 +388,6 @@ lka_filter_process_response(const char *name, const char *line)
*ep = 0;
kind = buffer;
-
if (strcmp(kind, "register") == 0)
return 1;
@@ -210,6 +400,17 @@ lka_filter_process_response(const char *name, const char *line)
return 0;
*ep = 0;
+ token = strtoull(qid, &ep, 16);
+ if (qid[0] == '\0' || *ep != '\0')
+ return 0;
+ if (errno == ERANGE && token == ULONG_MAX)
+ return 0;
+
+ qid = ep+1;
+ if ((ep = strchr(qid, '|')) == NULL)
+ return 0;
+ *ep = 0;
+
reqid = strtoull(qid, &ep, 16);
if (qid[0] == '\0' || *ep != '\0')
return 0;
@@ -223,7 +424,7 @@ lka_filter_process_response(const char *name, const char *line)
}
if (strcmp(kind, "filter-dataline") == 0) {
- filter_data_next(reqid, name, response);
+ filter_data_next(token, reqid, response);
return 1;
}
@@ -241,72 +442,157 @@ lka_filter_process_response(const char *name, const char *line)
parameter == NULL)
return 0;
- return lka_filter_response(reqid, response, parameter);
+ if (strcmp(response, "rewrite") == 0) {
+ filter_result_rewrite(reqid, parameter);
+ return 1;
+ }
+
+ if (strcmp(response, "reject") == 0) {
+ filter_result_reject(reqid, parameter);
+ return 1;
+ }
+
+ if (strcmp(response, "disconnect") == 0) {
+ filter_result_disconnect(reqid, parameter);
+ return 1;
+ }
+
+ filter_protocol_next(token, reqid, parameter);
+ return 1;
}
void
lka_filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param)
{
- struct filter_rule *rule;
- uint8_t i;
+ filter_protocol(reqid, phase, param);
+}
+
+void
+filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param)
+{
+ struct filter_session *fs;
+ struct filter_chain *filter_chain;
+ struct filter_entry *filter_entry;
+ struct filter *filter;
+ uint8_t i;
+
+ fs = tree_xget(&sessions, reqid);
+ filter_chain = dict_get(&filter_chains, fs->filter_name);
for (i = 0; i < nitems(filter_execs); ++i)
if (phase == filter_execs[i].phase)
break;
if (i == nitems(filter_execs))
goto proceed;
+ if (TAILQ_EMPTY(&filter_chain->chain[i]))
+ goto proceed;
- TAILQ_FOREACH(rule, &env->sc_filter_rules[phase], entry) {
- if (rule->proc) {
- filter_write(rule->proc, reqid,
+ fs->phase = phase;
+ TAILQ_FOREACH(filter_entry, &filter_chain->chain[i], entries) {
+ filter = dict_get(&filters, filter_entry->name);
+ if (filter->proc) {
+ filter_protocol_query(filter, filter_entry->id, reqid,
filter_execs[i].phase_name, param);
- return; /* deferred */
+ return; /* deferred */
}
- if (filter_execs[i].func(reqid, rule, param)) {
- if (rule->rewrite)
- filter_rewrite(reqid, rule->rewrite);
- else if (rule->disconnect)
- filter_disconnect(reqid, rule->disconnect);
+ if (filter_execs[i].func(filter, reqid, param)) {
+ if (filter->config->rewrite)
+ filter_result_rewrite(reqid, filter->config->rewrite);
+ else if (filter->config->disconnect)
+ filter_result_disconnect(reqid, filter->config->disconnect);
else
- filter_reject(reqid, rule->reject);
+ filter_result_reject(reqid, filter->config->reject);
return;
}
}
proceed:
- filter_proceed(reqid);
+ filter_result_proceed(reqid);
+}
+
+static void
+filter_protocol_next(uint64_t token, uint64_t reqid, const char *param)
+{
+ struct filter_session *fs;
+ struct filter_chain *filter_chain;
+ struct filter_entry *filter_entry;
+ struct filter *filter;
+
+ fs = tree_xget(&sessions, reqid);
+ filter_chain = dict_get(&filter_chains, fs->filter_name);
+
+ TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries)
+ if (filter_entry->id == token)
+ break;
+
+ while ((filter_entry = TAILQ_NEXT(filter_entry, entries))) {
+ filter = dict_get(&filters, filter_entry->name);
+ if (filter->proc) {
+ filter_protocol_query(filter, filter_entry->id, reqid,
+ filter_execs[fs->phase].phase_name, param);
+ return; /* deferred */
+ }
+
+ if (filter_execs[fs->phase].func(filter, reqid, param)) {
+ if (filter->config->rewrite)
+ filter_result_rewrite(reqid, filter->config->rewrite);
+ else if (filter->config->disconnect)
+ filter_result_disconnect(reqid, filter->config->disconnect);
+ else
+ filter_result_reject(reqid, filter->config->reject);
+ return;
+ }
+ }
+
+ filter_result_proceed(reqid);
}
+
static void
filter_data(uint64_t reqid, const char *line)
{
- struct filter_session *fs;
- struct filter_rule *rule;
+ struct filter_session *fs;
+ struct filter_chain *filter_chain;
+ struct filter_entry *filter_entry;
+ struct filter *filter;
fs = tree_xget(&sessions, reqid);
- rule = TAILQ_FIRST(&env->sc_filter_rules[FILTER_DATA_LINE]);
- filter_write_dataline(rule->proc, reqid, line);
+ fs->phase = FILTER_DATA_LINE;
+ filter_chain = dict_get(&filter_chains, fs->filter_name);
+ filter_entry = TAILQ_FIRST(&filter_chain->chain[fs->phase]);
+ if (filter_entry == NULL) {
+ io_printf(fs->io, "%s\r\n", line);
+ return;
+ }
+
+ filter = dict_get(&filters, filter_entry->name);
+ filter_data_query(filter, filter_entry->id, reqid, line);
}
static void
-filter_data_next(uint64_t reqid, const char *name, const char *line)
+filter_data_next(uint64_t token, uint64_t reqid, const char *line)
{
- struct filter_session *fs;
- struct filter_rule *rule;
+ struct filter_session *fs;
+ struct filter_chain *filter_chain;
+ struct filter_entry *filter_entry;
+ struct filter *filter;
fs = tree_xget(&sessions, reqid);
+ filter_chain = dict_get(&filter_chains, fs->filter_name);
- TAILQ_FOREACH(rule, &env->sc_filter_rules[FILTER_DATA_LINE], entry) {
- if (strcmp(rule->proc, name) == 0)
- break;
+ TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries)
+ if (filter_entry->id == token)
+ break;
+
+ if ((filter_entry = TAILQ_NEXT(filter_entry, entries))) {
+ filter = dict_get(&filters, filter_entry->name);
+ filter_data_query(filter, filter_entry->id, reqid, line);
+ return;
}
- if ((rule = TAILQ_NEXT(rule, entry)) == NULL)
- io_printf(fs->io, "%s\r\n", line);
- else
- filter_write_dataline(rule->proc, reqid, line);
+ io_printf(fs->io, "%s\r\n", line);
}
@@ -314,20 +600,20 @@ int
lka_filter_response(uint64_t reqid, const char *response, const char *param)
{
if (strcmp(response, "proceed") == 0)
- filter_proceed(reqid);
+ filter_result_proceed(reqid);
else if (strcmp(response, "rewrite") == 0)
- filter_rewrite(reqid, param);
+ filter_result_rewrite(reqid, param);
else if (strcmp(response, "reject") == 0)
- filter_reject(reqid, param);
+ filter_result_reject(reqid, param);
else if (strcmp(response, "disconnect") == 0)
- filter_disconnect(reqid, param);
+ filter_result_disconnect(reqid, param);
else
return 0;
return 1;
}
static void
-filter_write(const char *name, uint64_t reqid, const char *phase, const char *param)
+filter_protocol_query(struct filter *filter, uint64_t token, uint64_t reqid, const char *phase, const char *param)
{
int n;
time_t tm;
@@ -335,40 +621,40 @@ filter_write(const char *name, uint64_t reqid, const char *phase, const char *pa
fs = tree_xget(&sessions, reqid);
time(&tm);
- if (strcmp(phase, "connected") == 0)
- n = io_printf(lka_proc_get_io(name),
- "filter|%d|%zd|smtp-in|%s|%016"PRIx64"|%s|%s\n",
+ if (strcmp(phase, "connect") == 0)
+ n = io_printf(lka_proc_get_io(filter->proc),
+ "filter|%d|%zd|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s|%s\n",
PROTOCOL_VERSION,
tm,
- phase, reqid, fs->rdns, param);
+ phase, token, reqid, fs->rdns, param);
else
- n = io_printf(lka_proc_get_io(name),
- "filter|%d|%zd|smtp-in|%s|%016"PRIx64"|%s\n",
+ n = io_printf(lka_proc_get_io(filter->proc),
+ "filter|%d|%zd|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s\n",
PROTOCOL_VERSION,
tm,
- phase, reqid, param);
+ phase, token, reqid, param);
if (n == -1)
fatalx("failed to write to processor");
}
static void
-filter_write_dataline(const char *name, uint64_t reqid, const char *line)
+filter_data_query(struct filter *filter, uint64_t token, uint64_t reqid, const char *line)
{
int n;
time_t tm;
time(&tm);
- n = io_printf(lka_proc_get_io(name),
+ n = io_printf(lka_proc_get_io(filter->proc),
"filter|%d|%zd|smtp-in|data-line|"
- "%016"PRIx64"|%s\n",
+ "%016"PRIx64"|%016"PRIx64"|%s\n",
PROTOCOL_VERSION,
- tm, reqid, line);
+ tm, token, reqid, line);
if (n == -1)
fatalx("failed to write to processor");
}
static void
-filter_proceed(uint64_t reqid)
+filter_result_proceed(uint64_t reqid)
{
m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
m_add_id(p_pony, reqid);
@@ -377,7 +663,7 @@ filter_proceed(uint64_t reqid)
}
static void
-filter_rewrite(uint64_t reqid, const char *param)
+filter_result_rewrite(uint64_t reqid, const char *param)
{
m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
m_add_id(p_pony, reqid);
@@ -387,7 +673,7 @@ filter_rewrite(uint64_t reqid, const char *param)
}
static void
-filter_reject(uint64_t reqid, const char *message)
+filter_result_reject(uint64_t reqid, const char *message)
{
m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
m_add_id(p_pony, reqid);
@@ -397,7 +683,7 @@ filter_reject(uint64_t reqid, const char *message)
}
static void
-filter_disconnect(uint64_t reqid, const char *message)
+filter_result_disconnect(uint64_t reqid, const char *message)
{
m_create(p_pony, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
m_add_id(p_pony, reqid);
@@ -410,95 +696,95 @@ filter_disconnect(uint64_t reqid, const char *message)
/* below is code for builtin filters */
static int
-filter_check_table(struct filter_rule *rule, enum table_service kind, const char *key)
+filter_check_table(struct filter *filter, enum table_service kind, const char *key)
{
int ret = 0;
- if (rule->table) {
- if (table_lookup(rule->table, NULL, key, kind, NULL) > 0)
+ if (filter->config->table) {
+ if (table_lookup(filter->config->table, NULL, key, kind, NULL) > 0)
ret = 1;
- ret = rule->not_table < 0 ? !ret : ret;
+ ret = filter->config->not_table < 0 ? !ret : ret;
}
return ret;
}
static int
-filter_check_regex(struct filter_rule *rule, const char *key)
+filter_check_regex(struct filter *filter, const char *key)
{
int ret = 0;
- if (rule->regex) {
- if (table_lookup(rule->regex, NULL, key, K_REGEX, NULL) > 0)
+ if (filter->config->regex) {
+ if (table_lookup(filter->config->regex, NULL, key, K_REGEX, NULL) > 0)
ret = 1;
- ret = rule->not_regex < 0 ? !ret : ret;
+ ret = filter->config->not_regex < 0 ? !ret : ret;
}
return ret;
}
static int
-filter_check_fcrdns(struct filter_rule *rule, int fcrdns)
+filter_check_fcrdns(struct filter *filter, int fcrdns)
{
int ret = 0;
- if (rule->fcrdns) {
+ if (filter->config->fcrdns) {
ret = fcrdns == 0;
- ret = rule->not_fcrdns < 0 ? !ret : ret;
+ ret = filter->config->not_fcrdns < 0 ? !ret : ret;
}
return ret;
}
static int
-filter_check_rdns(struct filter_rule *rule, const char *hostname)
+filter_check_rdns(struct filter *filter, const char *hostname)
{
int ret = 0;
struct netaddr netaddr;
- if (rule->rdns) {
+ if (filter->config->rdns) {
/* if text_to_netaddress succeeds,
* we don't have an rDNS so the filter should match
*/
ret = text_to_netaddr(&netaddr, hostname);
- ret = rule->not_rdns < 0 ? !ret : ret;
+ ret = filter->config->not_rdns < 0 ? !ret : ret;
}
return ret;
}
static int
-filter_exec_notimpl(uint64_t reqid, struct filter_rule *rule, const char *param)
+filter_builtins_notimpl(struct filter *filter, uint64_t reqid, const char *param)
{
return 0;
}
static int
-filter_exec_connected(uint64_t reqid, struct filter_rule *rule, const char *param)
+filter_builtins_connect(struct filter *filter, uint64_t reqid, const char *param)
{
struct filter_session *fs;
fs = tree_xget(&sessions, reqid);
- if (filter_check_table(rule, K_NETADDR, param) ||
- filter_check_regex(rule, param) ||
- filter_check_rdns(rule, fs->rdns) ||
- filter_check_fcrdns(rule, fs->fcrdns))
+ if (filter_check_table(filter, K_NETADDR, param) ||
+ filter_check_regex(filter, param) ||
+ filter_check_rdns(filter, fs->rdns) ||
+ filter_check_fcrdns(filter, fs->fcrdns))
return 1;
return 0;
}
static int
-filter_exec_helo(uint64_t reqid, struct filter_rule *rule, const char *param)
+filter_builtins_helo(struct filter *filter, uint64_t reqid, const char *param)
{
struct filter_session *fs;
fs = tree_xget(&sessions, reqid);
- if (filter_check_table(rule, K_DOMAIN, param) ||
- filter_check_regex(rule, param) ||
- filter_check_rdns(rule, fs->rdns) ||
- filter_check_fcrdns(rule, fs->fcrdns))
+ if (filter_check_table(filter, K_DOMAIN, param) ||
+ filter_check_regex(filter, param) ||
+ filter_check_rdns(filter, fs->rdns) ||
+ filter_check_fcrdns(filter, fs->fcrdns))
return 1;
return 0;
}
static int
-filter_exec_mail_from(uint64_t reqid, struct filter_rule *rule, const char *param)
+filter_builtins_mail_from(struct filter *filter, uint64_t reqid, const char *param)
{
char buffer[SMTPD_MAXMAILADDRSIZE];
struct filter_session *fs;
@@ -508,16 +794,16 @@ filter_exec_mail_from(uint64_t reqid, struct filter_rule *rule, const char *para
buffer[strcspn(buffer, ">")] = '\0';
param = buffer;
- if (filter_check_table(rule, K_MAILADDR, param) ||
- filter_check_regex(rule, param) ||
- filter_check_rdns(rule, fs->rdns) ||
- filter_check_fcrdns(rule, fs->fcrdns))
+ if (filter_check_table(filter, K_MAILADDR, param) ||
+ filter_check_regex(filter, param) ||
+ filter_check_rdns(filter, fs->rdns) ||
+ filter_check_fcrdns(filter, fs->fcrdns))
return 1;
return 0;
}
static int
-filter_exec_rcpt_to(uint64_t reqid, struct filter_rule *rule, const char *param)
+filter_builtins_rcpt_to(struct filter *filter, uint64_t reqid, const char *param)
{
char buffer[SMTPD_MAXMAILADDRSIZE];
struct filter_session *fs;
@@ -527,10 +813,10 @@ filter_exec_rcpt_to(uint64_t reqid, struct filter_rule *rule, const char *param)
buffer[strcspn(buffer, ">")] = '\0';
param = buffer;
- if (filter_check_table(rule, K_MAILADDR, param) ||
- filter_check_regex(rule, param) ||
- filter_check_rdns(rule, fs->rdns) ||
- filter_check_fcrdns(rule, fs->fcrdns))
+ if (filter_check_table(filter, K_MAILADDR, param) ||
+ filter_check_regex(filter, param) ||
+ filter_check_rdns(filter, fs->rdns) ||
+ filter_check_fcrdns(filter, fs->fcrdns))
return 1;
return 0;
}
diff --git a/usr.sbin/smtpd/lka_proc.c b/usr.sbin/smtpd/lka_proc.c
index 73fd302b112..90fe9dbb441 100644
--- a/usr.sbin/smtpd/lka_proc.c
+++ b/usr.sbin/smtpd/lka_proc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka_proc.c,v 1.4 2018/12/06 13:57:06 gilles Exp $ */
+/* $OpenBSD: lka_proc.c,v 1.5 2018/12/21 14:33:52 gilles Exp $ */
/*
* Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
@@ -38,15 +38,28 @@
static int inited = 0;
static struct dict processors;
-
struct processor_instance {
char *name;
struct io *io;
+ int ready;
};
static void processor_io(struct io *, int, void *);
int lka_filter_process_response(const char *, const char *);
+int
+lka_proc_ready(void)
+{
+ void *iter;
+ struct processor_instance *pi;
+
+ iter = NULL;
+ while (dict_iter(&processors, &iter, NULL, (void **)&pi))
+ if (!pi->ready)
+ return 0;
+ return 1;
+}
+
void
lka_proc_forked(const char *name, int fd)
{
@@ -76,6 +89,29 @@ lka_proc_get_io(const char *name)
}
static void
+processor_register(const char *name, const char *line)
+{
+ struct processor_instance *processor;
+
+ processor = dict_xget(&processors, name);
+
+ if (strcasecmp(line, "register|ready") == 0) {
+ processor->ready = 1;
+ return;
+ }
+
+ if (strncasecmp(line, "register|report|", 16) == 0) {
+ lka_report_register_hook(name, line+16);
+ return;
+ }
+
+ if (strncasecmp(line, "register|filter|", 16) == 0) {
+ lka_filter_register_hook(name, line+16);
+ return;
+ }
+}
+
+static void
processor_io(struct io *io, int evt, void *arg)
{
const char *name = arg;
@@ -90,7 +126,9 @@ processor_io(struct io *io, int evt, void *arg)
if (line == NULL)
return;
- if (! lka_filter_process_response(name, line))
+ if (strncasecmp("register|", line, 9) == 0)
+ processor_register(name, line);
+ else if (! lka_filter_process_response(name, line))
fatalx("misbehaving filter");
goto nextline;
diff --git a/usr.sbin/smtpd/lka_report.c b/usr.sbin/smtpd/lka_report.c
index 30648aa1c50..e8eb33480a1 100644
--- a/usr.sbin/smtpd/lka_report.c
+++ b/usr.sbin/smtpd/lka_report.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka_report.c,v 1.15 2018/12/13 17:08:10 gilles Exp $ */
+/* $OpenBSD: lka_report.c,v 1.16 2018/12/21 14:33:52 gilles Exp $ */
/*
* Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
@@ -37,27 +37,127 @@
#define PROTOCOL_VERSION 1
+struct reporter_proc {
+ TAILQ_ENTRY(reporter_proc) entries;
+ const char *name;
+};
+TAILQ_HEAD(reporters, reporter_proc);
+
+static struct dict smtp_in;
+static struct dict smtp_out;
+
+static struct smtp_events {
+ const char *event;
+} smtp_events[] = {
+ { "link-connect" },
+ { "link-disconnect" },
+ { "link-identify" },
+ { "link-tls" },
+
+ { "tx-begin" },
+ { "tx-mail" },
+ { "tx-rcpt" },
+ { "tx-envelope" },
+ { "tx-data" },
+ { "tx-commit" },
+ { "tx-rollback" },
+
+ { "protocol-client" },
+ { "protocol-server" },
+
+ { "filter-response" },
+};
+
+
+void
+lka_report_init(void)
+{
+ struct reporters *tailq;
+ size_t i;
+
+ dict_init(&smtp_in);
+ dict_init(&smtp_out);
+
+ for (i = 0; i < nitems(smtp_events); ++i) {
+ tailq = xcalloc(1, sizeof (struct reporters *));
+ TAILQ_INIT(tailq);
+ dict_xset(&smtp_in, smtp_events[i].event, tailq);
+
+ tailq = xcalloc(1, sizeof (struct reporters *));
+ TAILQ_INIT(tailq);
+ dict_xset(&smtp_out, smtp_events[i].event, tailq);
+ }
+}
+
+void
+lka_report_register_hook(const char *name, const char *hook)
+{
+ struct dict *subsystem;
+ struct reporter_proc *rp;
+ struct reporters *tailq;
+ void *iter;
+ size_t i;
+
+ if (strncasecmp(hook, "smtp-in|", 8) == 0) {
+ subsystem = &smtp_in;
+ hook += 8;
+ }
+ else if (strncasecmp(hook, "smtp-out|", 9) == 0) {
+ subsystem = &smtp_out;
+ hook += 9;
+ }
+ else
+ return;
+
+ if (strcmp(hook, "*") == 0) {
+ iter = NULL;
+ while (dict_iter(subsystem, &iter, NULL, (void **)&tailq)) {
+ rp = xcalloc(1, sizeof *rp);
+ rp->name = xstrdup(name);
+ TAILQ_INSERT_TAIL(tailq, rp, entries);
+ }
+ return;
+ }
+
+ for (i = 0; i < nitems(smtp_events); i++)
+ if (strcmp(hook, smtp_events[i].event) == 0)
+ break;
+ if (i == nitems(smtp_events))
+ return;
+
+ tailq = dict_get(subsystem, hook);
+ rp = xcalloc(1, sizeof *rp);
+ rp->name = xstrdup(name);
+ TAILQ_INSERT_TAIL(tailq, rp, entries);
+}
+
static void
-report_smtp_broadcast(const char *direction, struct timeval *tv, const char *format, ...)
+report_smtp_broadcast(uint64_t reqid, const char *direction, struct timeval *tv, const char *event,
+ const char *format, ...)
{
va_list ap;
- void *hdl = NULL;
- const char *reporter;
struct dict *d;
+ struct reporters *tailq;
+ struct reporter_proc *rp;
if (strcmp("smtp-in", direction) == 0)
- d = env->sc_smtp_reporters_dict;
+ d = &smtp_in;
+
if (strcmp("smtp-out", direction) == 0)
- d = env->sc_mta_reporters_dict;
+ d = &smtp_out;
+
+ tailq = dict_xget(d, event);
+ TAILQ_FOREACH(rp, tailq, entries) {
+ if (!lka_filter_proc_in_session(reqid, rp->name))
+ continue;
- va_start(ap, format);
- while (dict_iter(d, &hdl, &reporter, NULL)) {
- if (io_printf(lka_proc_get_io(reporter), "report|%d|%lld.%06ld|%s|",
- PROTOCOL_VERSION, tv->tv_sec, tv->tv_usec, direction) == -1 ||
- io_vprintf(lka_proc_get_io(reporter), format, ap) == -1)
+ va_start(ap, format);
+ if (io_printf(lka_proc_get_io(rp->name), "report|%d|%lld.%06ld|%s|%s|",
+ PROTOCOL_VERSION, tv->tv_sec, tv->tv_usec, direction, event) == -1 ||
+ io_vprintf(lka_proc_get_io(rp->name), format, ap) == -1)
fatalx("failed to write to processor");
+ va_end(ap);
}
- va_end(ap);
}
void
@@ -97,37 +197,37 @@ lka_report_smtp_link_connect(const char *direction, struct timeval *tv, uint64_t
break;
}
- report_smtp_broadcast(direction, tv,
- "link-connect|%016"PRIx64"|%s|%s|%s:%d|%s:%d\n",
+ report_smtp_broadcast(reqid, direction, tv, "link-connect",
+ "%016"PRIx64"|%s|%s|%s:%d|%s:%d\n",
reqid, rdns, fcrdns_str, src, src_port, dest, dest_port);
}
void
lka_report_smtp_link_disconnect(const char *direction, struct timeval *tv, uint64_t reqid)
{
- report_smtp_broadcast(direction, tv,
- "link-disconnect|%016"PRIx64"\n", reqid);
+ report_smtp_broadcast(reqid, direction, tv, "link-disconnect",
+ "%016"PRIx64"\n", reqid);
}
void
lka_report_smtp_link_identify(const char *direction, struct timeval *tv, uint64_t reqid, const char *heloname)
{
- report_smtp_broadcast(direction, tv,
- "link-identify|%016"PRIx64"|%s\n", reqid, heloname);
+ report_smtp_broadcast(reqid, direction, tv, "link-identify",
+ "%016"PRIx64"|%s\n", reqid, heloname);
}
void
lka_report_smtp_link_tls(const char *direction, struct timeval *tv, uint64_t reqid, const char *ciphers)
{
- report_smtp_broadcast(direction, tv,
- "link-tls|%016"PRIx64"|%s\n", reqid, ciphers);
+ report_smtp_broadcast(reqid, direction, tv, "link-tls",
+ "%016"PRIx64"|%s\n", reqid, ciphers);
}
void
lka_report_smtp_tx_begin(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
{
- report_smtp_broadcast(direction, tv,
- "tx-begin|%016"PRIx64"|%08x\n", reqid, msgid);
+ report_smtp_broadcast(reqid, direction, tv, "tx-begin",
+ "%016"PRIx64"|%08x\n", reqid, msgid);
}
void
@@ -146,8 +246,8 @@ lka_report_smtp_tx_mail(const char *direction, struct timeval *tv, uint64_t reqi
result = "tempfail";
break;
}
- report_smtp_broadcast(direction, tv,
- "tx-mail|%016"PRIx64"|%08x|%s|%s\n", reqid, msgid, address, result);
+ report_smtp_broadcast(reqid, direction, tv, "tx-mail",
+ "%016"PRIx64"|%08x|%s|%s\n", reqid, msgid, address, result);
}
void
@@ -166,15 +266,15 @@ lka_report_smtp_tx_rcpt(const char *direction, struct timeval *tv, uint64_t reqi
result = "tempfail";
break;
}
- report_smtp_broadcast(direction, tv,
- "tx-rcpt|%016"PRIx64"|%08x|%s|%s\n", reqid, msgid, address, result);
+ report_smtp_broadcast(reqid, direction, tv, "tx-rcpt",
+ "%016"PRIx64"|%08x|%s|%s\n", reqid, msgid, address, result);
}
void
lka_report_smtp_tx_envelope(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, uint64_t evpid)
{
- report_smtp_broadcast(direction, tv,
- "tx-envelope|%016"PRIx64"|%08x|%016"PRIx64"\n",
+ report_smtp_broadcast(reqid, direction, tv, "tx-envelope",
+ "%016"PRIx64"|%08x|%016"PRIx64"\n",
reqid, msgid, evpid);
}
@@ -194,39 +294,39 @@ lka_report_smtp_tx_data(const char *direction, struct timeval *tv, uint64_t reqi
result = "tempfail";
break;
}
- report_smtp_broadcast(direction, tv,
- "tx-data|%016"PRIx64"|%08x|%s\n", reqid, msgid, result);
+ report_smtp_broadcast(reqid, direction, tv, "tx-data",
+ "%016"PRIx64"|%08x|%s\n", reqid, msgid, result);
}
void
lka_report_smtp_tx_commit(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, size_t msgsz)
{
- report_smtp_broadcast(direction, tv,
- "tx-commit|%016"PRIx64"|%08x|%zd\n",
+ report_smtp_broadcast(reqid, direction, tv, "tx-commit",
+ "%016"PRIx64"|%08x|%zd\n",
reqid, msgid, msgsz);
}
void
lka_report_smtp_tx_rollback(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
{
- report_smtp_broadcast(direction, tv,
- "tx-rollback|%016"PRIx64"|%08x\n",
+ report_smtp_broadcast(reqid, direction, tv, "tx-rollback",
+ "%016"PRIx64"|%08x\n",
reqid, msgid);
}
void
lka_report_smtp_protocol_client(const char *direction, struct timeval *tv, uint64_t reqid, const char *command)
{
- report_smtp_broadcast(direction, tv,
- "protocol-client|%016"PRIx64"|%s\n",
+ report_smtp_broadcast(reqid, direction, tv, "protocol-client",
+ "%016"PRIx64"|%s\n",
reqid, command);
}
void
lka_report_smtp_protocol_server(const char *direction, struct timeval *tv, uint64_t reqid, const char *response)
{
- report_smtp_broadcast(direction, tv,
- "protocol-server|%016"PRIx64"|%s\n",
+ report_smtp_broadcast(reqid, direction, tv, "protocol-server",
+ "%016"PRIx64"|%s\n",
reqid, response);
}
@@ -238,7 +338,7 @@ lka_report_smtp_filter_response(const char *direction, struct timeval *tv, uint6
const char *response_name;
switch (phase) {
- case FILTER_CONNECTED:
+ case FILTER_CONNECT:
phase_name = "connected";
break;
case FILTER_HELO:
@@ -304,7 +404,7 @@ lka_report_smtp_filter_response(const char *direction, struct timeval *tv, uint6
response_name = "";
}
- report_smtp_broadcast(direction, tv,
- "filter-response|%016"PRIx64"|%s|%s|%s\n",
- reqid, phase_name, response_name, param ? param : "");
+ report_smtp_broadcast(reqid, direction, tv, "filter-response",
+ "%016"PRIx64"|%s|%s%s%s\n",
+ reqid, phase_name, response_name, param ? "|" : "", param ? param : "");
}
diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y
index dbf8c21b634..bb437d34f15 100644
--- a/usr.sbin/smtpd/parse.y
+++ b/usr.sbin/smtpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.237 2018/12/13 14:43:31 gilles Exp $ */
+/* $OpenBSD: parse.y,v 1.238 2018/12/21 14:33:52 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -106,7 +106,8 @@ static struct ca *sca;
struct dispatcher *dispatcher;
struct rule *rule;
struct processor *processor;
-struct filter_rule *filter_rule;
+struct filter_config *filter_config;
+/*static uint64_t last_dynproc_id = 1;*/
enum listen_options {
LO_FAMILY = 0x000001,
@@ -172,8 +173,8 @@ typedef struct {
%}
%token ACTION ALIAS ANY ARROW AUTH AUTH_OPTIONAL
-%token BACKUP BOUNCE
-%token CA CERT CHROOT CIPHERS COMMIT COMPRESSION CONNECT
+%token BACKUP BOUNCE BUILTIN
+%token CA CERT CHAIN CHROOT CIPHERS COMMIT COMPRESSION CONNECT
%token CHECK_FCRDNS CHECK_RDNS CHECK_REGEX CHECK_TABLE
%token DATA DATA_LINE DHE DISCONNECT DOMAIN
%token EHLO ENABLE ENCRYPTION ERROR EXPAND_ONLY
@@ -187,7 +188,7 @@ typedef struct {
%token MAIL_FROM MAILDIR MASK_SRC MASQUERADE MATCH MAX_MESSAGE_SIZE MAX_DEFERRED MBOX MDA MTA MX
%token NO_DSN NO_VERIFY NOOP
%token ON
-%token PKI PORT PROC
+%token PKI PORT PROC PROC_EXEC
%token QUEUE QUIT
%token RCPT_TO RECIPIENT RECEIVEDAUTH RELAY REJECT REPORT REWRITE RSET
%token SCHEDULER SENDER SENDERS SMTP SMTP_IN SMTP_OUT SMTPS SOCKET SRC SUB_ADDR_DELIM
@@ -1116,38 +1117,38 @@ MATCH {
filter_action_builtin:
REJECT STRING {
- filter_rule->reject = $2;
+ filter_config->reject = $2;
}
| DISCONNECT STRING {
- filter_rule->disconnect = $2;
+ filter_config->disconnect = $2;
}
;
filter_phase_check_table:
negation CHECK_TABLE tables {
- filter_rule->not_table = $1 ? -1 : 1;
- filter_rule->table = $3;
+ filter_config->not_table = $1 ? -1 : 1;
+ filter_config->table = $3;
}
;
filter_phase_check_regex:
negation CHECK_REGEX tables {
- filter_rule->not_regex = $1 ? -1 : 1;
- filter_rule->regex = $3;
+ filter_config->not_regex = $1 ? -1 : 1;
+ filter_config->regex = $3;
}
;
filter_phase_check_fcrdns:
negation CHECK_FCRDNS {
- filter_rule->not_fcrdns = $1 ? -1 : 1;
- filter_rule->fcrdns = 1;
+ filter_config->not_fcrdns = $1 ? -1 : 1;
+ filter_config->fcrdns = 1;
}
;
filter_phase_check_rdns:
negation CHECK_RDNS {
- filter_rule->not_rdns = $1 ? -1 : 1;
- filter_rule->rdns = 1;
+ filter_config->not_rdns = $1 ? -1 : 1;
+ filter_config->rdns = 1;
}
;
@@ -1156,7 +1157,7 @@ filter_phase_check_table | filter_phase_check_regex | filter_phase_check_fcrdns
filter_phase_connect:
CONNECT {
- filter_rule->phase = FILTER_CONNECTED;
+ filter_config->phase = FILTER_CONNECT;
} filter_phase_connect_options filter_action_builtin
;
@@ -1165,13 +1166,13 @@ filter_phase_check_table | filter_phase_check_regex | filter_phase_check_fcrdns
filter_phase_helo:
HELO {
- filter_rule->phase = FILTER_HELO;
+ filter_config->phase = FILTER_HELO;
} filter_phase_helo_options filter_action_builtin
;
filter_phase_ehlo:
EHLO {
- filter_rule->phase = FILTER_EHLO;
+ filter_config->phase = FILTER_EHLO;
} filter_phase_helo_options filter_action_builtin
;
@@ -1180,7 +1181,7 @@ filter_phase_check_table | filter_phase_check_regex | filter_phase_check_fcrdns
filter_phase_mail_from:
MAIL_FROM {
- filter_rule->phase = FILTER_MAIL_FROM;
+ filter_config->phase = FILTER_MAIL_FROM;
} filter_phase_mail_from_options filter_action_builtin
;
@@ -1189,47 +1190,48 @@ filter_phase_check_table | filter_phase_check_regex | filter_phase_check_fcrdns
filter_phase_rcpt_to:
RCPT_TO {
- filter_rule->phase = FILTER_RCPT_TO;
+ filter_config->phase = FILTER_RCPT_TO;
} filter_phase_rcpt_to_options filter_action_builtin
;
filter_phase_data:
DATA {
- filter_rule->phase = FILTER_DATA;
+ filter_config->phase = FILTER_DATA;
} filter_action_builtin
;
filter_phase_data_line:
DATA_LINE {
- filter_rule->phase = FILTER_DATA_LINE;
+ filter_config->phase = FILTER_DATA_LINE;
} filter_action_builtin
;
filter_phase_quit:
QUIT {
- filter_rule->phase = FILTER_QUIT;
+ filter_config->phase = FILTER_QUIT;
} filter_action_builtin
;
filter_phase_rset:
RSET {
- filter_rule->phase = FILTER_RSET;
+ filter_config->phase = FILTER_RSET;
} filter_action_builtin
;
filter_phase_noop:
NOOP {
- filter_rule->phase = FILTER_NOOP;
+ filter_config->phase = FILTER_NOOP;
} filter_action_builtin
;
filter_phase_commit:
COMMIT {
- filter_rule->phase = FILTER_COMMIT;
+ filter_config->phase = FILTER_COMMIT;
} filter_action_builtin
;
+
filter_phase:
filter_phase_connect
| filter_phase_helo
@@ -1244,28 +1246,131 @@ filter_phase_connect
| filter_phase_commit
;
-filter:
-FILTER SMTP_IN {
- filter_rule = xcalloc(1, sizeof *filter_rule);
-} filter_phase {
- TAILQ_INSERT_TAIL(&conf->sc_filter_rules[filter_rule->phase], filter_rule, entry);
- filter_rule = NULL;
+
+filterel:
+STRING {
+ struct filter_config *fr;
+ size_t i;
+
+ if ((fr = dict_get(conf->sc_filters_dict, $1)) == NULL) {
+ yyerror("no filter exist with that name: %s", $1);
+ free($1);
+ YYERROR;
+ }
+ if (fr->filter_type == FILTER_TYPE_CHAIN) {
+ yyerror("no filter chain allowed within a filter chain: %s", $1);
+ free($1);
+ YYERROR;
+ }
+
+ for (i = 0; i < filter_config->chain_size; i++) {
+ if (strcmp(filter_config->chain[i], $1) == 0) {
+ yyerror("no filter allowed twice within a filter chain: %s", $1);
+ free($1);
+ YYERROR;
+ }
+ }
+
+ if (fr->proc) {
+ if (dict_check(&filter_config->chain_procs, fr->proc)) {
+ yyerror("no proc allowed twice within a filter chain: %s", fr->proc);
+ free($1);
+ YYERROR;
+ }
+ dict_set(&filter_config->chain_procs, fr->proc, NULL);
+ }
+
+ filter_config->chain_size += 1;
+ filter_config->chain = reallocarray(filter_config->chain, filter_config->chain_size, sizeof(char *));
+ if (filter_config->chain == NULL)
+ err(1, NULL);
+ filter_config->chain[filter_config->chain_size - 1] = $1;
}
-| FILTER SMTP_IN ON STRING {
+;
+
+filter_list:
+filterel
+| filterel comma filter_list
+;
+
+filter:
+/*
+FILTER STRING PROC STRING {
+ if (dict_get(conf->sc_filters_dict, $2)) {
+ yyerror("filter already exists with that name: %s", $2);
+ free($2);
+ free($4);
+ YYERROR;
+ }
if (! dict_get(conf->sc_processors_dict, $4)) {
yyerror("no processor exist with that name: %s", $4);
free($4);
YYERROR;
}
- dict_set(conf->sc_smtp_reporters_dict, $4, (void *)~0);
+
+ filter_config = xcalloc(1, sizeof *filter_config);
+ filter_config->filter_type = FILTER_TYPE_PROC;
+ filter_config->name = $2;
+ filter_config->proc = $4;
+ dict_set(conf->sc_filters_dict, $2, filter_config);
+ filter_config = NULL;
}
-| FILTER SMTP_OUT ON STRING {
- if (! dict_get(conf->sc_processors_dict, $4)) {
- yyerror("no processor exist with that name: %s", $4);
+|
+FILTER STRING PROC_EXEC STRING {
+ char buffer[128];
+
+ do {
+ (void)snprintf(buffer, sizeof buffer, "<dynproc:%016"PRIx64">", last_dynproc_id++);
+ } while (dict_check(conf->sc_processors_dict, buffer));
+
+ if (dict_get(conf->sc_filters_dict, $2)) {
+ yyerror("filter already exists with that name: %s", $2);
+ free($2);
free($4);
YYERROR;
}
- dict_set(conf->sc_mta_reporters_dict, $4, (void *)~0);
+
+ processor = xcalloc(1, sizeof *processor);
+ processor->command = $4;
+
+ filter_config = xcalloc(1, sizeof *filter_config);
+ filter_config->filter_type = FILTER_TYPE_PROC;
+ filter_config->name = $2;
+ filter_config->proc = xstrdup(buffer);
+ dict_set(conf->sc_filters_dict, $2, filter_config);
+} proc_params {
+ dict_set(conf->sc_processors_dict, filter_config->proc, processor);
+ processor = NULL;
+ filter_config = NULL;
+}
+|
+*/
+FILTER STRING BUILTIN {
+ if (dict_get(conf->sc_filters_dict, $2)) {
+ yyerror("filter already exists with that name: %s", $2);
+ free($2);
+ YYERROR;
+ }
+ filter_config = xcalloc(1, sizeof *filter_config);
+ filter_config->name = $2;
+ filter_config->filter_type = FILTER_TYPE_BUILTIN;
+ dict_set(conf->sc_filters_dict, $2, filter_config);
+} filter_phase {
+ filter_config = NULL;
+}
+|
+FILTER STRING CHAIN {
+ if (dict_get(conf->sc_filters_dict, $2)) {
+ yyerror("filter already exists with that name: %s", $2);
+ free($2);
+ YYERROR;
+ }
+ filter_config = xcalloc(1, sizeof *filter_config);
+ filter_config->filter_type = FILTER_TYPE_CHAIN;
+ dict_init(&filter_config->chain_procs);
+} '{' filter_list '}' {
+ dict_set(conf->sc_filters_dict, $2, filter_config);
+ filter_config = NULL;
}
;
@@ -1409,12 +1514,19 @@ limits_scheduler: opt_limit_scheduler limits_scheduler
;
-opt_sock_listen : FILTER {
+opt_sock_listen : FILTER STRING {
if (listen_opts.options & LO_FILTER) {
yyerror("filter already specified");
+ free($2);
+ YYERROR;
+ }
+ if (dict_get(conf->sc_filters_dict, $2) == NULL) {
+ yyerror("no filter exist with that name: %s", $2);
+ free($2);
YYERROR;
}
listen_opts.options |= LO_FILTER;
+ listen_opts.filtername = $2;
}
| MASK_SRC {
if (config_lo_mask_source(&listen_opts)) {
@@ -1470,12 +1582,18 @@ opt_if_listen : INET4 {
}
listen_opts.port = $2;
}
- | FILTER {
+ | FILTER STRING {
if (listen_opts.options & LO_FILTER) {
yyerror("filter already specified");
YYERROR;
}
+ if (dict_get(conf->sc_filters_dict, $2) == NULL) {
+ yyerror("no filter exist with that name: %s", $2);
+ free($2);
+ YYERROR;
+ }
listen_opts.options |= LO_FILTER;
+ listen_opts.filtername = $2;
}
| SMTPS {
if (listen_opts.options & LO_SSL) {
@@ -1818,8 +1936,10 @@ lookup(char *s)
{ "auth-optional", AUTH_OPTIONAL },
{ "backup", BACKUP },
{ "bounce", BOUNCE },
+ { "builtin", BUILTIN },
{ "ca", CA },
{ "cert", CERT },
+ { "chain", CHAIN },
{ "check-fcrdns", CHECK_FCRDNS },
{ "check-rdns", CHECK_RDNS },
{ "check-regex", CHECK_REGEX },
@@ -1874,6 +1994,7 @@ lookup(char *s)
{ "pki", PKI },
{ "port", PORT },
{ "proc", PROC },
+ { "proc-exec", PROC_EXEC },
{ "queue", QUEUE },
{ "quit", QUIT },
{ "rcpt-to", RCPT_TO },
@@ -2454,8 +2575,12 @@ config_listener(struct listener *h, struct listen_opts *lo)
if (lo->hostname == NULL)
lo->hostname = conf->sc_hostname;
- if (lo->options & LO_FILTER)
+ if (lo->options & LO_FILTER) {
h->flags |= F_FILTERED;
+ (void)strlcpy(h->filter_name,
+ lo->filtername,
+ sizeof(h->filter_name));
+ }
h->pki_name[0] = '\0';
diff --git a/usr.sbin/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c
index 8e518be334d..08e537e5b71 100644
--- a/usr.sbin/smtpd/smtp_session.c
+++ b/usr.sbin/smtpd/smtp_session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtp_session.c,v 1.377 2018/12/20 19:57:30 gilles Exp $ */
+/* $OpenBSD: smtp_session.c,v 1.378 2018/12/21 14:33:52 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -169,8 +169,7 @@ struct smtp_session {
((s)->listener->flags & F_FILTERED)
#define SESSION_DATA_FILTERED(s) \
- (((s)->listener->flags & F_FILTERED) && \
- TAILQ_FIRST(&env->sc_filter_rules[FILTER_DATA_LINE]))
+ ((s)->listener->flags & F_FILTERED)
static int smtp_mailaddr(struct mailaddr *, char *, int, char **, const char *);
@@ -1032,7 +1031,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
report_smtp_filter_response("smtp-in", s->id, s->filter_phase,
filter_response,
filter_param == s->filter_param ? NULL : filter_param);
- if (s->filter_phase == FILTER_CONNECTED) {
+ if (s->filter_phase == FILTER_CONNECT) {
smtp_proceed_connected(s);
return;
}
@@ -1093,7 +1092,7 @@ smtp_io(struct io *io, int evt, void *arg)
switch (evt) {
case IO_TLSREADY:
- log_info("%016"PRIx64" smtp tls address=%s host=%s ciphers=%s",
+ log_info("%016"PRIx64" smtp tls address=%s host=%s ciphers=\"%s\"",
s->id, ss_to_text(&s->ss), s->hostname, ssl_to_text(io_ssl(s->io)));
report_smtp_link_tls("smtp-in", s->id, ssl_to_text(io_ssl(s->io)));
@@ -1593,25 +1592,12 @@ smtp_check_noparam(struct smtp_session *s, const char *args)
static void
smtp_query_filters(enum filter_phase phase, struct smtp_session *s, const char *args)
{
- uint8_t i;
-
- if (TAILQ_FIRST(&env->sc_filter_rules[phase])) {
- m_create(p_lka, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_int(p_lka, phase);
- m_add_string(p_lka, args);
- m_close(p_lka);
- tree_xset(&wait_filters, s->id, s);
- return;
- }
-
- if (phase == FILTER_CONNECTED) {
- smtp_proceed_connected(s);
- return;
- }
- for (i = 0; i < nitems(commands); ++i)
- if (commands[i].filter_phase == phase)
- commands[i].proceed(s, args);
+ m_create(p_lka, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
+ m_add_id(p_lka, s->id);
+ m_add_int(p_lka, phase);
+ m_add_string(p_lka, args);
+ m_close(p_lka);
+ tree_xset(&wait_filters, s->id, s);
}
static void
@@ -1622,6 +1608,7 @@ smtp_filter_begin(struct smtp_session *s)
m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1);
m_add_id(p_lka, s->id);
+ m_add_string(p_lka, s->listener->filter_name);
m_add_sockaddr(p_lka, (struct sockaddr *)&s->ss);
m_add_sockaddr(p_lka, (struct sockaddr *)&s->listener->ss);
m_add_string(p_lka, s->hostname);
@@ -1682,7 +1669,7 @@ smtp_filter_phase(enum filter_phase phase, struct smtp_session *s, const char *p
return;
}
- if (s->filter_phase == FILTER_CONNECTED) {
+ if (s->filter_phase == FILTER_CONNECT) {
smtp_proceed_connected(s);
return;
}
@@ -1991,11 +1978,12 @@ smtp_connected(struct smtp_session *s)
log_info("%016"PRIx64" smtp connected address=%s host=%s",
s->id, ss_to_text(&s->ss), s->hostname);
+ smtp_filter_begin(s);
+
report_smtp_link_connect("smtp-in", s->id, s->hostname, s->fcrdns, &s->ss,
&s->listener->ss);
- smtp_filter_begin(s);
- smtp_filter_phase(FILTER_CONNECTED, s, ss_to_text(&s->ss));
+ smtp_filter_phase(FILTER_CONNECT, s, ss_to_text(&s->ss));
}
static void
@@ -2883,7 +2871,7 @@ filter_session_io(struct io *io, int evt, void *arg)
char*line = NULL;
ssize_t len;
- log_trace(TRACE_IO, "filter session: %p: %s %s", tx, io_strevent(evt),
+ log_trace(TRACE_IO, "filter session io (smtp): %p: %s %s", tx, io_strevent(evt),
io_strio(io));
switch (evt) {
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index ca83cd51a13..143859c3ca2 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.594 2018/12/13 17:08:10 gilles Exp $ */
+/* $OpenBSD: smtpd.h,v 1.595 2018/12/21 14:33:52 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -410,7 +410,7 @@ enum expand_type {
};
enum filter_phase {
- FILTER_CONNECTED = 0,
+ FILTER_CONNECT,
FILTER_HELO,
FILTER_EHLO,
FILTER_STARTTLS,
@@ -526,6 +526,7 @@ struct listener {
in_port_t port;
struct timeval timeout;
struct event ev;
+ char filter_name[PATH_MAX];
char pki_name[PATH_MAX];
char ca_name[PATH_MAX];
char tag[SMTPD_TAG_SIZE];
@@ -582,8 +583,6 @@ struct smtpd {
size_t sc_scheduler_max_schedule;
struct dict *sc_processors_dict;
- struct dict *sc_smtp_reporters_dict;
- struct dict *sc_mta_reporters_dict;
int sc_ttl;
#define MAX_BOUNCE_WARN 4
@@ -600,8 +599,9 @@ struct smtpd {
TAILQ_HEAD(listenerlist, listener) *sc_listeners;
TAILQ_HEAD(rulelist, rule) *sc_rules;
- TAILQ_HEAD(filterrules, filter_rule) sc_filter_rules[FILTER_PHASES_COUNT];
+
+ struct dict *sc_filters_dict;
struct dict *sc_dispatchers;
struct dispatcher *sc_dispatcher_bounce;
@@ -1039,15 +1039,25 @@ struct processor {
const char *chroot;
};
-struct filter_rule {
- TAILQ_ENTRY(filter_rule) entry;
+enum filter_type {
+ FILTER_TYPE_BUILTIN,
+ FILTER_TYPE_PROC,
+ FILTER_TYPE_CHAIN,
+};
+struct filter_config {
+ char *name;
+ enum filter_type filter_type;
enum filter_phase phase;
char *reject;
char *disconnect;
char *rewrite;
char *proc;
+ const char **chain;
+ size_t chain_size;
+ struct dict chain_procs;
+
int8_t not_table;
struct table *table;
@@ -1322,11 +1332,14 @@ int lka(void);
/* lka_proc.c */
+int lka_proc_ready(void);
void lka_proc_forked(const char *, int);
struct io *lka_proc_get_io(const char *);
/* lka_report.c */
+void lka_report_init(void);
+void lka_report_register_hook(const char *, const char *);
void lka_report_smtp_link_connect(const char *, struct timeval *, uint64_t, const char *, int,
const struct sockaddr_storage *, const struct sockaddr_storage *);
void lka_report_smtp_link_disconnect(const char *, struct timeval *, uint64_t);
@@ -1346,7 +1359,11 @@ void lka_report_smtp_filter_response(const char *, struct timeval *, uint64_t,
/* lka_filter.c */
-void lka_filter_begin(uint64_t, const struct sockaddr_storage *, const struct sockaddr_storage *, const char *, int);
+void lka_filter_init(void);
+void lka_filter_register_hook(const char *, const char *);
+void lka_filter_ready(void);
+int lka_filter_proc_in_session(uint64_t, const char *);
+void lka_filter_begin(uint64_t, const char *, const struct sockaddr_storage *, const struct sockaddr_storage *, const char *, int);
void lka_filter_end(uint64_t);
void lka_filter_protocol(uint64_t, enum filter_phase, const char *);
void lka_filter_data_begin(uint64_t);