aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGilles Chehade <gilles@poolp.org>2018-12-21 23:10:25 +0100
committerGilles Chehade <gilles@poolp.org>2018-12-21 23:10:25 +0100
commit179976e04d51e717308219c70b053ee6c3f0f72b (patch)
tree21d7be8ba6ceeff2b232e1eaa51f51b42970299b
parentMerge pull request #882 from bsdsx/strndup_compat (diff)
parentsync (diff)
downloadOpenSMTPD-179976e04d51e717308219c70b053ee6c3f0f72b.tar.xz
OpenSMTPD-179976e04d51e717308219c70b053ee6c3f0f72b.zip
Merge branch 'master' into portable
-rw-r--r--smtpd/config.c16
-rw-r--r--smtpd/lka.c40
-rw-r--r--smtpd/lka_filter.c611
-rw-r--r--smtpd/lka_proc.c47
-rw-r--r--smtpd/lka_report.c184
-rw-r--r--smtpd/mail.maildir.c6
-rw-r--r--smtpd/mproc.c7
-rw-r--r--smtpd/mta_session.c114
-rw-r--r--smtpd/parse.y428
-rw-r--r--smtpd/ruleset.c38
-rw-r--r--smtpd/smtp_session.c162
-rw-r--r--smtpd/smtpd.conf.547
-rw-r--r--smtpd/smtpd.h62
-rw-r--r--smtpd/ssl.c4
-rw-r--r--smtpd/table.c10
15 files changed, 1382 insertions, 394 deletions
diff --git a/smtpd/config.c b/smtpd/config.c
index 64ca6786..c1089104 100644
--- a/smtpd/config.c
+++ b/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>
@@ -50,7 +50,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;
@@ -92,9 +91,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 ||
@@ -107,9 +105,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;
@@ -121,8 +118,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);
@@ -131,8 +126,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;
@@ -166,8 +159,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/smtpd/lka.c b/smtpd/lka.c
index 4073350f..519fb1d7 100644
--- a/smtpd/lka.c
+++ b/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>
@@ -64,6 +64,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)
{
@@ -93,6 +97,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;
@@ -375,8 +380,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:
@@ -605,13 +613,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:
@@ -703,6 +712,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");
@@ -713,6 +725,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/smtpd/lka_filter.c b/smtpd/lka_filter.c
index 48288d50..f33962db 100644
--- a/smtpd/lka_filter.c
+++ b/smtpd/lka_filter.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka_filter.c,v 1.14 2018/12/11 13:40:30 gilles Exp $ */
+/* $OpenBSD: lka_filter.c,v 1.21 2018/12/21 20:38:42 gilles Exp $ */
/*
* Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
@@ -37,62 +37,256 @@
#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 *);
+#define PROTOCOL_VERSION 1
+
+struct filter;
+struct filter_session;
+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_data(uint64_t reqid, const char *line);
+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 void filter_write(const char *, uint64_t, const char *, const char *);
-static void filter_write_dataline(const char *, uint64_t, const char *);
+static int filter_builtins_notimpl(struct filter_session *, struct filter *, uint64_t, const char *);
+static int filter_builtins_connect(struct filter_session *, struct filter *, uint64_t, const char *);
+static int filter_builtins_helo(struct filter_session *, struct filter *, uint64_t, const char *);
+static int filter_builtins_mail_from(struct filter_session *, struct filter *, uint64_t, const char *);
+static int filter_builtins_rcpt_to(struct filter_session *, struct filter *, 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 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 },
-};
-
-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;
+};
+
+static struct filter_exec {
+ enum filter_phase phase;
+ const char *phase_name;
+ int (*func)(struct filter_session *, 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 },
+};
+
+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:
+ break;
+ }
+ }
+
+ iter = NULL;
+ while (dict_iter(env->sc_filters_dict, &iter, &name, (void **)&filter_config)) {
+ switch (filter_config->filter_type) {
+ 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_xget(&filters, filter_config->chain[i]);
+ dict_set(&filters, name, filter);
+ break;
+
+ case FILTER_TYPE_BUILTIN:
+ case FILTER_TYPE_PROC:
+ 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;
+
+ if ((fs = tree_get(&sessions, reqid)) == NULL)
+ return 0;
+
+ 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,
@@ -107,6 +301,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);
@@ -135,7 +330,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]);
@@ -154,8 +350,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
@@ -179,6 +377,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;
}
}
@@ -186,6 +389,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;
@@ -199,9 +403,23 @@ lka_filter_process_response(const char *name, const char *line)
*ep = 0;
kind = buffer;
+ if (strcmp(kind, "register") == 0)
+ return 1;
+
if (strcmp(kind, "filter-result") != 0 &&
strcmp(kind, "filter-dataline") != 0)
- return 1;
+ return 0;
+
+ qid = ep+1;
+ if ((ep = strchr(qid, '|')) == NULL)
+ 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)
@@ -221,7 +439,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;
}
@@ -239,72 +457,162 @@ 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(fs, 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;
+
+ /* client session may have disappeared while we were in proc */
+ if ((fs = tree_xget(&sessions, reqid)) == NULL)
+ return;
+
+ 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(fs, 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);
+ /* client session may have disappeared while we were in proc */
+ if ((fs = tree_get(&sessions, reqid)) == NULL)
+ return;
- TAILQ_FOREACH(rule, &env->sc_filter_rules[FILTER_DATA_LINE], entry) {
- if (strcmp(rule->proc, name) == 0)
- break;
+ 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;
+
+ 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);
}
@@ -312,20 +620,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;
@@ -333,40 +641,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);
@@ -375,7 +683,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);
@@ -385,7 +693,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);
@@ -395,7 +703,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);
@@ -408,127 +716,124 @@ 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_rdns_table(struct filter *filter, enum table_service kind, const char *key)
+{
+ int ret = 0;
+
+ if (filter->config->rdns_table) {
+ if (table_lookup(filter->config->rdns_table, NULL, key, kind, NULL) > 0)
+ ret = 1;
+ ret = filter->config->not_rdns_table < 0 ? !ret : ret;
+ }
+ return ret;
+}
+
+static int
+filter_check_rdns_regex(struct filter *filter, const char *key)
{
int ret = 0;
- if (rule->table) {
- if (table_lookup(rule->table, NULL, key, kind, NULL) > 0)
+ if (filter->config->rdns_regex) {
+ if (table_lookup(filter->config->rdns_regex, NULL, key, K_REGEX, NULL) > 0)
ret = 1;
- ret = rule->not_table < 0 ? !ret : ret;
+ ret = filter->config->not_rdns_regex < 0 ? !ret : ret;
}
return ret;
}
static int
-filter_check_regex(struct filter_rule *rule, const char *key)
+filter_check_src_table(struct filter *filter, enum table_service kind, const char *key)
{
int ret = 0;
- if (rule->regex) {
- if (table_lookup(rule->regex, NULL, key, K_REGEX, NULL) > 0)
+ if (filter->config->src_table) {
+ if (table_lookup(filter->config->src_table, NULL, key, kind, NULL) > 0)
ret = 1;
- ret = rule->not_regex < 0 ? !ret : ret;
+ ret = filter->config->not_src_table < 0 ? !ret : ret;
}
return ret;
}
static int
-filter_check_fcrdns(struct filter_rule *rule, int fcrdns)
+filter_check_src_regex(struct filter *filter, const char *key)
{
int ret = 0;
- if (rule->fcrdns) {
+ if (filter->config->src_regex) {
+ if (table_lookup(filter->config->src_regex, NULL, key, K_REGEX, NULL) > 0)
+ ret = 1;
+ ret = filter->config->not_src_regex < 0 ? !ret : ret;
+ }
+ return ret;
+}
+
+static int
+filter_check_fcrdns(struct filter *filter, int fcrdns)
+{
+ int ret = 0;
+
+ 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_session *fs, 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_global(struct filter_session *fs, 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_fcrdns(filter, fs->fcrdns) ||
+ filter_check_rdns(filter, fs->rdns) ||
+ filter_check_rdns_table(filter, K_DOMAIN, fs->rdns) ||
+ filter_check_rdns_regex(filter, fs->rdns) ||
+ filter_check_src_table(filter, K_NETADDR, ss_to_text(&fs->ss_src)) ||
+ filter_check_src_regex(filter, ss_to_text(&fs->ss_src)))
return 1;
return 0;
}
static int
-filter_exec_helo(uint64_t reqid, struct filter_rule *rule, const char *param)
+filter_builtins_connect(struct filter_session *fs, 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))
- return 1;
- return 0;
+ return filter_builtins_global(fs, filter, reqid, param);
}
static int
-filter_exec_mail_from(uint64_t reqid, struct filter_rule *rule, const char *param)
+filter_builtins_helo(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
{
- char buffer[SMTPD_MAXMAILADDRSIZE];
- struct filter_session *fs;
-
- fs = tree_xget(&sessions, reqid);
- (void)strlcpy(buffer, param+1, sizeof(buffer));
- 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))
- return 1;
- return 0;
+ return filter_builtins_global(fs, filter, reqid, param);
}
static int
-filter_exec_rcpt_to(uint64_t reqid, struct filter_rule *rule, const char *param)
+filter_builtins_mail_from(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
{
- char buffer[SMTPD_MAXMAILADDRSIZE];
- struct filter_session *fs;
+ return filter_builtins_global(fs, filter, reqid, param);
+}
- fs = tree_xget(&sessions, reqid);
- (void)strlcpy(buffer, param+1, sizeof(buffer));
- 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))
- return 1;
- return 0;
+static int
+filter_builtins_rcpt_to(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param)
+{
+ return filter_builtins_global(fs, filter, reqid, param);
}
diff --git a/smtpd/lka_proc.c b/smtpd/lka_proc.c
index fbf8b855..eafebbec 100644
--- a/smtpd/lka_proc.c
+++ b/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.6 2018/12/21 19:07:47 gilles Exp $ */
/*
* Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
@@ -40,15 +40,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)
{
@@ -62,6 +75,9 @@ lka_proc_forked(const char *name, int fd)
processor = xcalloc(1, sizeof *processor);
processor->name = xstrdup(name);
processor->io = io_new();
+
+ io_set_nonblocking(fd);
+
io_set_fd(processor->io, fd);
io_set_callback(processor->io, processor_io, processor->name);
dict_xset(&processors, name, processor);
@@ -78,6 +94,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;
@@ -92,7 +131,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/smtpd/lka_report.c b/smtpd/lka_report.c
index 3986cbb8..ddf7c7cf 100644
--- a/smtpd/lka_report.c
+++ b/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>
@@ -39,27 +39,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
@@ -99,37 +199,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
@@ -148,8 +248,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
@@ -168,15 +268,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);
}
@@ -196,39 +296,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);
}
@@ -240,7 +340,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:
@@ -306,7 +406,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/smtpd/mail.maildir.c b/smtpd/mail.maildir.c
index a7b2e765..637d3f72 100644
--- a/smtpd/mail.maildir.c
+++ b/smtpd/mail.maildir.c
@@ -116,6 +116,7 @@ maildir_engine(const char *dirname, int junk)
char extpath[PATH_MAX];
char subdir[PATH_MAX];
char filename[PATH_MAX];
+ char hostname[HOST_NAME_MAX+1];
char tmp[PATH_MAX];
char new[PATH_MAX];
@@ -167,10 +168,13 @@ maildir_engine(const char *dirname, int junk)
}
}
+ if (gethostname(hostname, sizeof hostname) != 0)
+ (void)strlcpy(hostname, "localhost", sizeof hostname);
+
(void)snprintf(filename, sizeof filename, "%lld.%08x.%s",
(long long int) time(NULL),
arc4random(),
- "localhost");
+ hostname);
(void)snprintf(tmp, sizeof tmp, "%s/tmp/%s", dirname, filename);
diff --git a/smtpd/mproc.c b/smtpd/mproc.c
index 53b2795d..e1bf324f 100644
--- a/smtpd/mproc.c
+++ b/smtpd/mproc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mproc.c,v 1.31 2018/12/13 17:08:10 gilles Exp $ */
+/* $OpenBSD: mproc.c,v 1.32 2018/12/17 08:56:31 eric Exp $ */
/*
* Copyright (c) 2012 Eric Faurot <eric@faurot.net>
@@ -584,6 +584,11 @@ m_get_data(struct msg *m, const void **data, size_t *sz)
{
m_get_size(m, sz);
+ if (*sz == 0) {
+ *data = NULL;
+ return;
+ }
+
if (m->pos + *sz > m->end)
m_error("msg too short");
diff --git a/smtpd/mta_session.c b/smtpd/mta_session.c
index b366b37a..655c205f 100644
--- a/smtpd/mta_session.c
+++ b/smtpd/mta_session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mta_session.c,v 1.113 2018/10/31 15:13:21 gilles Exp $ */
+/* $OpenBSD: mta_session.c,v 1.114 2018/12/17 11:14:56 eric Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -152,6 +152,10 @@ static void mta_response(struct mta_session *, char *);
static const char * mta_strstate(int);
static void mta_start_tls(struct mta_session *);
static int mta_verify_certificate(struct mta_session *);
+static void mta_cert_init(struct mta_session *);
+static void mta_cert_init_cb(void *, int, const char *, const void *, size_t);
+static void mta_cert_verify(struct mta_session *);
+static void mta_cert_verify_cb(void *, int);
static void mta_tls_verified(struct mta_session *);
static struct mta_session *mta_tree_pop(struct tree *, uint64_t);
static const char * dsn_strret(enum dsn_ret);
@@ -939,7 +943,7 @@ mta_response(struct mta_session *s, char *line)
return;
}
- mta_start_tls(s);
+ mta_cert_init(s);
break;
case MTA_AUTH_PLAIN:
@@ -1151,7 +1155,7 @@ mta_io(struct io *io, int evt, void *arg)
if (s->use_smtps) {
io_set_write(io);
- mta_start_tls(s);
+ mta_cert_init(s);
}
else {
mta_enter_state(s, MTA_BANNER);
@@ -1164,12 +1168,7 @@ mta_io(struct io *io, int evt, void *arg)
s->id, ssl_to_text(io_ssl(s->io)));
s->flags |= MTA_TLS;
- if (mta_verify_certificate(s)) {
- io_pause(s->io, IO_IN);
- break;
- }
-
- mta_tls_verified(s);
+ mta_cert_verify(s);
break;
case IO_DATAIN:
@@ -1658,6 +1657,103 @@ mta_verify_certificate(struct mta_session *s)
}
static void
+mta_cert_init(struct mta_session *s)
+{
+ const char *name;
+ int fallback;
+
+ if (s->relay->pki_name) {
+ name = s->relay->pki_name;
+ fallback = 0;
+ }
+ else {
+ name = s->helo;
+ fallback = 1;
+ }
+
+ if (cert_init(name, fallback, mta_cert_init_cb, s)) {
+ tree_xset(&wait_ssl_init, s->id, s);
+ s->flags |= MTA_WAIT;
+ }
+}
+
+static void
+mta_cert_init_cb(void *arg, int status, const char *name, const void *cert,
+ size_t cert_len)
+{
+ struct mta_session *s = arg;
+ void *ssl;
+ char *xname = NULL, *xcert = NULL;
+
+ if (s->flags & MTA_WAIT)
+ mta_tree_pop(&wait_ssl_init, s->id);
+
+ if (status == CA_FAIL && s->relay->pki_name) {
+ log_info("%016"PRIx64" mta closing reason=ca-failure", s->id);
+ mta_free(s);
+ return;
+ }
+
+ if (name)
+ xname = xstrdup(name);
+ if (cert)
+ xcert = xmemdup(cert, cert_len);
+ ssl = ssl_mta_init(xname, xcert, cert_len, env->sc_tls_ciphers);
+ free(xname);
+ free(xcert);
+ if (ssl == NULL)
+ fatal("mta: ssl_mta_init");
+ io_start_tls(s->io, ssl);
+}
+
+static void
+mta_cert_verify(struct mta_session *s)
+{
+ const char *name;
+ int fallback;
+
+ if (s->relay->ca_name) {
+ name = s->relay->ca_name;
+ fallback = 0;
+ }
+ else {
+ name = s->helo;
+ fallback = 1;
+ }
+
+ if (cert_verify(io_ssl(s->io), name, fallback, mta_cert_verify_cb, s)) {
+ tree_xset(&wait_ssl_verify, s->id, s);
+ io_pause(s->io, IO_IN);
+ s->flags |= MTA_WAIT;
+ }
+}
+
+static void
+mta_cert_verify_cb(void *arg, int status)
+{
+ struct mta_session *s = arg;
+ int resume = 0;
+
+ if (s->flags & MTA_WAIT) {
+ mta_tree_pop(&wait_ssl_verify, s->id);
+ resume = 1;
+ }
+
+ if (status == CERT_OK)
+ s->flags |= MTA_TLS_VERIFIED;
+ else if (s->relay->flags & RELAY_TLS_VERIFY) {
+ errno = 0;
+ mta_error(s, "SSL certificate check failed");
+ mta_free(s);
+ return;
+ }
+
+ mta_tls_verified(s);
+ if (resume)
+ io_resume(s->io, IO_IN);
+}
+
+static void
mta_tls_verified(struct mta_session *s)
{
X509 *x;
diff --git a/smtpd/parse.y b/smtpd/parse.y
index d3c9b427..b778b76f 100644
--- a/smtpd/parse.y
+++ b/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.241 2018/12/21 21:35:29 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -112,7 +112,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,
@@ -178,9 +179,9 @@ typedef struct {
%}
%token ACTION ALIAS ANY ARROW AUTH AUTH_OPTIONAL
-%token BACKUP BOUNCE
-%token CA CERT CHROOT CIPHERS COMMIT COMPRESSION CONNECT
-%token CHECK_FCRDNS CHECK_RDNS CHECK_REGEX CHECK_TABLE
+%token BACKUP BOUNCE BUILTIN
+%token CA CERT CHAIN CHROOT CIPHERS COMMIT COMPRESSION CONNECT
+%token CHECK_FCRDNS CHECK_RDNS CHECK_RDNS_REGEX CHECK_RDNS_TABLE CHECK_SRC_REGEX CHECK_SRC_TABLE
%token DATA DATA_LINE DHE DISCONNECT DOMAIN
%token EHLO ENABLE ENCRYPTION ERROR EXPAND_ONLY
%token FILTER FOR FORWARD_ONLY FROM
@@ -193,9 +194,9 @@ 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 RCPT_TO RECIPIENT RECEIVEDAUTH REGEX RELAY REJECT REPORT REWRITE RSET
%token SCHEDULER SENDER SENDERS SMTP SMTP_IN SMTP_OUT SMTPS SOCKET SRC SUB_ADDR_DELIM
%token TABLE TAG TAGGED TLS TLS_REQUIRE TTL
%token USER USERBASE
@@ -913,6 +914,25 @@ negation TAG tables {
rule->flag_tag = $1 ? -1 : 1;
rule->table_tag = strdup(t->t_name);
}
+|
+negation TAG REGEX tables {
+ struct table *t = $4;
+
+ if (rule->flag_tag) {
+ yyerror("tag already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for tag lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_tag = $1 ? -1 : 1;
+ rule->flag_tag_regex = 1;
+ rule->table_tag = strdup(t->t_name);
+}
| negation HELO tables {
struct table *t = $3;
@@ -930,6 +950,24 @@ negation TAG tables {
rule->flag_smtp_helo = $1 ? -1 : 1;
rule->table_smtp_helo = strdup(t->t_name);
}
+| negation HELO REGEX tables {
+ struct table *t = $4;
+
+ if (rule->flag_smtp_helo) {
+ yyerror("mail-helo already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for helo lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_smtp_helo = $1 ? -1 : 1;
+ rule->flag_smtp_helo_regex = 1;
+ rule->table_smtp_helo = strdup(t->t_name);
+}
| negation TLS {
if (rule->flag_smtp_starttls) {
yyerror("tls already specified for this rule");
@@ -961,6 +999,24 @@ negation TAG tables {
rule->flag_smtp_auth = $1 ? -1 : 1;
rule->table_smtp_auth = strdup(t->t_name);
}
+| negation AUTH REGEX tables {
+ struct table *t = $4;
+
+ if (rule->flag_smtp_auth) {
+ yyerror("auth already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for auth lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_smtp_auth = $1 ? -1 : 1;
+ rule->flag_smtp_auth_regex = 1;
+ rule->table_smtp_auth = strdup(t->t_name);
+}
| negation MAIL_FROM tables {
struct table *t = $3;
@@ -978,6 +1034,24 @@ negation TAG tables {
rule->flag_smtp_mail_from = $1 ? -1 : 1;
rule->table_smtp_mail_from = strdup(t->t_name);
}
+| negation MAIL_FROM REGEX tables {
+ struct table *t = $4;
+
+ if (rule->flag_smtp_mail_from) {
+ yyerror("mail-from already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for mail-from lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_smtp_mail_from = $1 ? -1 : 1;
+ rule->flag_smtp_mail_from_regex = 1;
+ rule->table_smtp_mail_from = strdup(t->t_name);
+}
| negation RCPT_TO tables {
struct table *t = $3;
@@ -995,6 +1069,24 @@ negation TAG tables {
rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
rule->table_smtp_rcpt_to = strdup(t->t_name);
}
+| negation RCPT_TO REGEX tables {
+ struct table *t = $4;
+
+ if (rule->flag_smtp_rcpt_to) {
+ yyerror("rcpt-to already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for rcpt-to lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
+ rule->flag_smtp_rcpt_to_regex = 1;
+ rule->table_smtp_rcpt_to = strdup(t->t_name);
+}
| negation FROM SOCKET {
if (rule->flag_from) {
@@ -1041,6 +1133,24 @@ negation TAG tables {
rule->flag_from = $1 ? -1 : 1;
rule->table_from = strdup(t->t_name);
}
+| negation FROM SRC REGEX tables {
+ struct table *t = $5;
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for from lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_from = $1 ? -1 : 1;
+ rule->flag_from_regex = 1;
+ rule->table_from = strdup(t->t_name);
+}
| negation FOR LOCAL {
struct table *t = table_find(conf, "<localnames>", NULL);
@@ -1079,6 +1189,24 @@ negation TAG tables {
rule->flag_for = $1 ? -1 : 1;
rule->table_for = strdup(t->t_name);
}
+| negation FOR DOMAIN REGEX tables {
+ struct table *t = $5;
+
+ if (rule->flag_for) {
+ yyerror("for already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for 'for' lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_for = $1 ? -1 : 1;
+ rule->flag_for_regex = 1;
+ rule->table_for = strdup(t->t_name);
+}
;
match_options:
@@ -1122,120 +1250,160 @@ 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_phase_check_fcrdns:
+negation CHECK_FCRDNS {
+ filter_config->not_fcrdns = $1 ? -1 : 1;
+ filter_config->fcrdns = 1;
}
;
-filter_phase_check_regex:
-negation CHECK_REGEX tables {
- filter_rule->not_regex = $1 ? -1 : 1;
- filter_rule->regex = $3;
+filter_phase_check_rdns:
+negation CHECK_RDNS {
+ filter_config->not_rdns = $1 ? -1 : 1;
+ filter_config->rdns = 1;
}
;
-filter_phase_check_fcrdns:
-negation CHECK_FCRDNS {
- filter_rule->not_fcrdns = $1 ? -1 : 1;
- filter_rule->fcrdns = 1;
+filter_phase_check_rdns_table:
+negation CHECK_RDNS_TABLE tables {
+ filter_config->not_rdns_table = $1 ? -1 : 1;
+ filter_config->rdns_table = $3;
+}
+;
+filter_phase_check_rdns_regex:
+negation CHECK_RDNS_REGEX tables {
+ filter_config->not_rdns_regex = $1 ? -1 : 1;
+ filter_config->rdns_regex = $3;
}
;
-filter_phase_check_rdns:
-negation CHECK_RDNS {
- filter_rule->not_rdns = $1 ? -1 : 1;
- filter_rule->rdns = 1;
+filter_phase_check_src_table:
+negation CHECK_SRC_TABLE tables {
+ filter_config->not_src_table = $1 ? -1 : 1;
+ filter_config->src_table = $3;
+}
+;
+filter_phase_check_src_regex:
+negation CHECK_SRC_REGEX tables {
+ filter_config->not_src_regex = $1 ? -1 : 1;
+ filter_config->src_regex = $3;
}
;
+filter_phase_global_options:
+filter_phase_check_fcrdns |
+filter_phase_check_rdns |
+filter_phase_check_rdns_regex |
+filter_phase_check_rdns_table |
+filter_phase_check_src_regex |
+filter_phase_check_src_table;
+
filter_phase_connect_options:
-filter_phase_check_table | filter_phase_check_regex | filter_phase_check_fcrdns | filter_phase_check_rdns;
+filter_phase_global_options;
+
+filter_phase_helo_options:
+filter_phase_global_options;
+
+filter_phase_mail_from_options:
+filter_phase_global_options;
+
+filter_phase_rcpt_to_options:
+filter_phase_global_options;
+
+filter_phase_data_options:
+filter_phase_global_options;
+
+filter_phase_quit_options:
+filter_phase_global_options;
+
+filter_phase_rset_options:
+filter_phase_global_options;
+
+filter_phase_noop_options:
+filter_phase_global_options;
+
+filter_phase_commit_options:
+filter_phase_global_options;
+
filter_phase_connect:
CONNECT {
- filter_rule->phase = FILTER_CONNECTED;
+ filter_config->phase = FILTER_CONNECT;
} filter_phase_connect_options filter_action_builtin
;
-filter_phase_helo_options:
-filter_phase_check_table | filter_phase_check_regex | filter_phase_check_fcrdns | filter_phase_check_rdns;
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
;
-filter_phase_mail_from_options:
-filter_phase_check_table | filter_phase_check_regex | filter_phase_check_fcrdns | filter_phase_check_rdns;
-
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
;
-filter_phase_rcpt_to_options:
-filter_phase_check_table | filter_phase_check_regex | filter_phase_check_fcrdns | filter_phase_check_rdns;
-
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_action_builtin
+ filter_config->phase = FILTER_DATA;
+} filter_phase_data_options 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_action_builtin
+ filter_config->phase = FILTER_QUIT;
+} filter_phase_quit_options filter_action_builtin
;
filter_phase_rset:
RSET {
- filter_rule->phase = FILTER_RSET;
-} filter_action_builtin
+ filter_config->phase = FILTER_RSET;
+} filter_phase_rset_options filter_action_builtin
;
filter_phase_noop:
NOOP {
- filter_rule->phase = FILTER_NOOP;
-} filter_action_builtin
+ filter_config->phase = FILTER_NOOP;
+} filter_phase_noop_options filter_action_builtin
;
filter_phase_commit:
COMMIT {
- filter_rule->phase = FILTER_COMMIT;
-} filter_action_builtin
+ filter_config->phase = FILTER_COMMIT;
+} filter_phase_commit_options filter_action_builtin
;
+
filter_phase:
filter_phase_connect
| filter_phase_helo
@@ -1243,35 +1411,136 @@ filter_phase_connect
| filter_phase_mail_from
| filter_phase_rcpt_to
| filter_phase_data
-| filter_phase_data_line
+/*| filter_phase_data_line*/
| filter_phase_quit
| filter_phase_noop
| filter_phase_rset
| 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;
}
;
@@ -1415,12 +1684,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)) {
@@ -1476,12 +1752,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) {
@@ -1824,12 +2106,16 @@ 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 },
- { "check-table", CHECK_TABLE },
+ { "check-rdns-regex", CHECK_RDNS_REGEX },
+ { "check-rdns-table", CHECK_RDNS_TABLE },
+ { "check-src-regex", CHECK_SRC_REGEX },
+ { "check-src-table", CHECK_SRC_TABLE },
{ "chroot", CHROOT },
{ "ciphers", CIPHERS },
{ "commit", COMMIT },
@@ -1880,11 +2166,13 @@ lookup(char *s)
{ "pki", PKI },
{ "port", PORT },
{ "proc", PROC },
+ { "proc-exec", PROC_EXEC },
{ "queue", QUEUE },
{ "quit", QUIT },
{ "rcpt-to", RCPT_TO },
{ "received-auth", RECEIVEDAUTH },
{ "recipient", RECIPIENT },
+ { "regex", REGEX },
{ "reject", REJECT },
{ "relay", RELAY },
{ "rset", RSET },
@@ -2462,8 +2750,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/smtpd/ruleset.c b/smtpd/ruleset.c
index 1ad5ee36..70a155ef 100644
--- a/smtpd/ruleset.c
+++ b/smtpd/ruleset.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ruleset.c,v 1.36 2018/06/16 19:41:26 gilles Exp $ */
+/* $OpenBSD: ruleset.c,v 1.37 2018/12/21 21:35:29 gilles Exp $ */
/*
* Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
@@ -57,12 +57,16 @@ ruleset_match_tag(struct rule *r, const struct envelope *evp)
{
int ret;
struct table *table;
+ enum table_service service = K_STRING;
if (!r->flag_tag)
return 1;
+ if (r->flag_tag_regex)
+ service = K_REGEX;
+
table = table_find(env, r->table_tag, NULL);
- if ((ret = ruleset_match_table_lookup(table, evp->tag, K_STRING)) < 0)
+ if ((ret = ruleset_match_table_lookup(table, evp->tag, service)) < 0)
return ret;
return r->flag_tag < 0 ? !ret : ret;
@@ -74,6 +78,7 @@ ruleset_match_from(struct rule *r, const struct envelope *evp)
int ret;
const char *key;
struct table *table;
+ enum table_service service = K_NETADDR;
if (!r->flag_from)
return 1;
@@ -89,8 +94,11 @@ ruleset_match_from(struct rule *r, const struct envelope *evp)
else
key = ss_to_text(&evp->ss);
+ if (r->flag_from_regex)
+ service = K_REGEX;
+
table = table_find(env, r->table_from, NULL);
- if ((ret = ruleset_match_table_lookup(table, key, K_NETADDR)) < 0)
+ if ((ret = ruleset_match_table_lookup(table, key, service)) < 0)
return -1;
return r->flag_from < 0 ? !ret : ret;
@@ -101,13 +109,17 @@ ruleset_match_to(struct rule *r, const struct envelope *evp)
{
int ret;
struct table *table;
+ enum table_service service = K_DOMAIN;
if (!r->flag_for)
return 1;
+ if (r->flag_for_regex)
+ service = K_REGEX;
+
table = table_find(env, r->table_for, NULL);
if ((ret = ruleset_match_table_lookup(table, evp->dest.domain,
- K_DOMAIN)) < 0)
+ service)) < 0)
return -1;
return r->flag_for < 0 ? !ret : ret;
@@ -118,12 +130,16 @@ ruleset_match_smtp_helo(struct rule *r, const struct envelope *evp)
{
int ret;
struct table *table;
+ enum table_service service = K_DOMAIN;
if (!r->flag_smtp_helo)
return 1;
+ if (r->flag_smtp_helo_regex)
+ service = K_REGEX;
+
table = table_find(env, r->table_smtp_helo, NULL);
- if ((ret = ruleset_match_table_lookup(table, evp->helo, K_DOMAIN)) < 0)
+ if ((ret = ruleset_match_table_lookup(table, evp->helo, service)) < 0)
return -1;
return r->flag_smtp_helo < 0 ? !ret : ret;
@@ -171,15 +187,19 @@ ruleset_match_smtp_mail_from(struct rule *r, const struct envelope *evp)
int ret;
const char *key;
struct table *table;
+ enum table_service service = K_MAILADDR;
if (!r->flag_smtp_mail_from)
return 1;
+ if (r->flag_smtp_mail_from_regex)
+ service = K_REGEX;
+
if ((key = mailaddr_to_text(&evp->sender)) == NULL)
return -1;
table = table_find(env, r->table_smtp_mail_from, NULL);
- if ((ret = ruleset_match_table_lookup(table, key, K_MAILADDR)) < 0)
+ if ((ret = ruleset_match_table_lookup(table, key, service)) < 0)
return -1;
return r->flag_smtp_mail_from < 0 ? !ret : ret;
@@ -191,15 +211,19 @@ ruleset_match_smtp_rcpt_to(struct rule *r, const struct envelope *evp)
int ret;
const char *key;
struct table *table;
+ enum table_service service = K_MAILADDR;
if (!r->flag_smtp_rcpt_to)
return 1;
+ if (r->flag_smtp_rcpt_to_regex)
+ service = K_REGEX;
+
if ((key = mailaddr_to_text(&evp->dest)) == NULL)
return -1;
table = table_find(env, r->table_smtp_rcpt_to, NULL);
- if ((ret = ruleset_match_table_lookup(table, key, K_MAILADDR)) < 0)
+ if ((ret = ruleset_match_table_lookup(table, key, service)) < 0)
return -1;
return r->flag_smtp_rcpt_to < 0 ? !ret : ret;
diff --git a/smtpd/smtp_session.c b/smtpd/smtp_session.c
index 4bbd325f..a166d8c9 100644
--- a/smtpd/smtp_session.c
+++ b/smtpd/smtp_session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtp_session.c,v 1.375 2018/12/14 09:18:03 eric Exp $ */
+/* $OpenBSD: smtp_session.c,v 1.379 2018/12/21 14:41:41 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -108,6 +108,7 @@ enum smtp_command {
struct smtp_rcpt {
TAILQ_ENTRY(smtp_rcpt) entry;
+ uint64_t evpid;
struct mailaddr maddr;
size_t destcount;
};
@@ -175,8 +176,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 *);
@@ -839,6 +839,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
s = tree_xget(&wait_lka_rcpt, reqid);
if (success) {
m_get_evpid(&m, &evpid);
+ s->tx->evp.id = evpid;
s->tx->destcount++;
report_smtp_tx_envelope("smtp-in", s->id, s->tx->msgid, evpid);
}
@@ -867,6 +868,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
}
else {
rcpt = xcalloc(1, sizeof(*rcpt));
+ rcpt->evpid = s->tx->evp.id;
rcpt->destcount = s->tx->destcount;
rcpt->maddr = s->tx->evp.rcpt;
TAILQ_INSERT_TAIL(&s->tx->rcpts, rcpt, entry);
@@ -897,21 +899,24 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS),
s->tx->msgid);
+ log_info("%016"PRIx64" smtp message "
+ "msgid=%08x size=%zu nrcpt=%zu proto=%s",
+ s->id,
+ s->tx->msgid,
+ s->tx->odatalen,
+ s->tx->rcptcount,
+ s->flags & SF_EHLO ? "ESMTP" : "SMTP");
TAILQ_FOREACH(rcpt, &s->tx->rcpts, entry) {
- log_info("%016"PRIx64" smtp message address=%s host=%s "
- "msgid=%08x from=<%s%s%s> to=<%s%s%s> size=%zu ndest=%zu proto=%s",
+ log_info("%016"PRIx64" smtp envelope "
+ "evpid=%016"PRIx64" from=<%s%s%s> to=<%s%s%s>",
s->id,
- ss_to_text(&s->ss), s->hostname,
- s->tx->msgid,
+ rcpt->evpid,
s->tx->evp.sender.user,
s->tx->evp.sender.user[0] == '\0' ? "" : "@",
s->tx->evp.sender.domain,
rcpt->maddr.user,
rcpt->maddr.user[0] == '\0' ? "" : "@",
- rcpt->maddr.domain,
- s->tx->odatalen,
- rcpt->destcount,
- s->flags & SF_EHLO ? "ESMTP" : "SMTP");
+ rcpt->maddr.domain);
}
smtp_tx_free(s->tx);
s->mailcount++;
@@ -928,26 +933,26 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
strnvis(user, s->username, sizeof user, VIS_WHITE | VIS_SAFE);
if (success == LKA_OK) {
log_info("%016"PRIx64" smtp "
- "authentication user=%s address=%s "
- "host=%s result=ok",
- s->id, user, ss_to_text(&s->ss), s->hostname);
+ "authentication user=%s "
+ "result=ok",
+ s->id, user);
s->flags |= SF_AUTHENTICATED;
smtp_reply(s, "235 %s: Authentication succeeded",
esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
}
else if (success == LKA_PERMFAIL) {
log_info("%016"PRIx64" smtp "
- "authentication user=%s address=%s "
- "host=%s result=permfail",
- s->id, user, ss_to_text(&s->ss), s->hostname);
+ "authentication user=%s "
+ "result=permfail",
+ s->id, user);
smtp_auth_failure_pause(s);
return;
}
else if (success == LKA_TEMPFAIL) {
log_info("%016"PRIx64" smtp "
- "authentication user=%s address=%s "
- "host=%s result=tempfail",
- s->id, user, ss_to_text(&s->ss), s->hostname);
+ "authentication user=%s "
+ "result=tempfail",
+ s->id, user);
smtp_reply(s, "421 %s: Temporary failure",
esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
}
@@ -962,9 +967,9 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
s = tree_xpop(&wait_ssl_init, resp_ca_cert->reqid);
if (resp_ca_cert->status == CA_FAIL) {
- log_info("%016"PRIx64" smtp disconnected address=%s host=%s "
+ log_info("%016"PRIx64" smtp disconnected "
"reason=ca-failure",
- s->id, ss_to_text(&s->ss), s->hostname);
+ s->id);
smtp_free(s, "CA failure");
return;
}
@@ -989,8 +994,8 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
s->flags |= SF_VERIFIED;
else if (s->listener->flags & F_TLS_VERIFY) {
log_info("%016"PRIx64" smtp "
- "disconnected address=%s host=%s reason=cert-check-failed",
- s->id, ss_to_text(&s->ss), s->hostname);
+ "disconnected reason=cert-check-failed",
+ s->id);
smtp_free(s, "SSL certificate check failed");
return;
}
@@ -1038,7 +1043,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;
}
@@ -1068,8 +1073,8 @@ smtp_tls_verified(struct smtp_session *s)
x = SSL_get_peer_certificate(io_ssl(s->io));
if (x) {
log_info("%016"PRIx64" smtp "
- "client-cert-check address=%s host=%s result=\"%s\"",
- s->id, ss_to_text(&s->ss), s->hostname,
+ "client-cert-check result=\"%s\"",
+ s->id,
(s->flags & SF_VERIFIED) ? "success" : "failure");
X509_free(x);
}
@@ -1099,8 +1104,8 @@ 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\"",
- s->id, ss_to_text(&s->ss), s->hostname, ssl_to_text(io_ssl(s->io)));
+ log_info("%016"PRIx64" smtp tls ciphers=%s",
+ s->id, ssl_to_text(io_ssl(s->io)));
report_smtp_link_tls("smtp-in", s->id, ssl_to_text(io_ssl(s->io)));
@@ -1170,9 +1175,9 @@ smtp_io(struct io *io, int evt, void *arg)
case IO_LOWAT:
if (s->state == STATE_QUIT) {
- log_info("%016"PRIx64" smtp disconnected address=%s host=%s "
+ log_info("%016"PRIx64" smtp disconnected "
"reason=quit",
- s->id, ss_to_text(&s->ss), s->hostname);
+ s->id);
smtp_free(s, "done");
break;
}
@@ -1187,23 +1192,23 @@ smtp_io(struct io *io, int evt, void *arg)
break;
case IO_TIMEOUT:
- log_info("%016"PRIx64" smtp disconnected address=%s host=%s "
+ log_info("%016"PRIx64" smtp disconnected "
"reason=timeout",
- s->id, ss_to_text(&s->ss), s->hostname);
+ s->id);
smtp_free(s, "timeout");
break;
case IO_DISCONNECTED:
- log_info("%016"PRIx64" smtp disconnected address=%s host=%s "
+ log_info("%016"PRIx64" smtp disconnected "
"reason=disconnect",
- s->id, ss_to_text(&s->ss), s->hostname);
+ s->id);
smtp_free(s, "disconnected");
break;
case IO_ERROR:
- log_info("%016"PRIx64" smtp disconnected address=%s host=%s "
+ log_info("%016"PRIx64" smtp disconnected "
"reason=\"io-error: %s\"",
- s->id, ss_to_text(&s->ss), s->hostname, io_error(io));
+ s->id, io_error(io));
smtp_free(s, "IO error");
break;
@@ -1320,18 +1325,26 @@ smtp_command(struct smtp_session *s, char *line)
* ANY
*/
case CMD_QUIT:
+ if (!smtp_check_noparam(s, args))
+ break;
smtp_filter_phase(FILTER_QUIT, s, NULL);
break;
case CMD_NOOP:
+ if (!smtp_check_noparam(s, args))
+ break;
smtp_filter_phase(FILTER_NOOP, s, NULL);
break;
case CMD_HELP:
+ if (!smtp_check_noparam(s, args))
+ break;
smtp_proceed_help(s, NULL);
break;
case CMD_WIZ:
+ if (!smtp_check_noparam(s, args))
+ break;
smtp_proceed_wiz(s, NULL);
break;
@@ -1346,6 +1359,9 @@ smtp_command(struct smtp_session *s, char *line)
static int
smtp_check_rset(struct smtp_session *s, const char *args)
{
+ if (!smtp_check_noparam(s, args))
+ return 0;
+
if (s->helo[0] == '\0') {
smtp_reply(s, "503 %s %s: Command not allowed at this point.",
esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
@@ -1553,6 +1569,9 @@ smtp_check_rcpt_to(struct smtp_session *s, const char *args)
static int
smtp_check_data(struct smtp_session *s, const char *args)
{
+ if (!smtp_check_noparam(s, args))
+ return 0;
+
if (s->tx == NULL) {
smtp_reply(s, "503 %s %s: Command not allowed at this point.",
esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
@@ -1573,31 +1592,24 @@ smtp_check_data(struct smtp_session *s, const char *args)
static int
smtp_check_noparam(struct smtp_session *s, const char *args)
{
+ if (args != NULL) {
+ smtp_reply(s, "500 %s %s: command does not accept arguments.",
+ esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
+ esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
+ return 0;
+ }
return 1;
}
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
@@ -1608,6 +1620,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);
@@ -1668,7 +1681,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;
}
@@ -1977,11 +1990,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
@@ -2057,33 +2071,33 @@ smtp_reply(struct smtp_session *s, char *fmt, ...)
if (s->flags & SF_BADINPUT) {
log_info("%016"PRIx64" smtp "
- "bad-input address=%s host=%s result=\"%.*s\"",
- s->id, ss_to_text(&s->ss), s->hostname, n, buf);
+ "bad-input result=\"%.*s\"",
+ s->id, n, buf);
}
else if (s->state == STATE_AUTH_INIT) {
log_info("%016"PRIx64" smtp "
- "failed-command address=%s host=%s "
+ "failed-command "
"command=\"AUTH PLAIN (...)\" result=\"%.*s\"",
- s->id, ss_to_text(&s->ss), s->hostname, n, buf);
+ s->id, n, buf);
}
else if (s->state == STATE_AUTH_USERNAME) {
log_info("%016"PRIx64" smtp "
- "failed-command address=%s host=%s "
+ "failed-command "
"command=\"AUTH LOGIN (username)\" result=\"%.*s\"",
- s->id, ss_to_text(&s->ss), s->hostname, n, buf);
+ s->id, n, buf);
}
else if (s->state == STATE_AUTH_PASSWORD) {
log_info("%016"PRIx64" smtp "
- "failed-command address=%s host=%s "
+ "failed-command "
"command=\"AUTH LOGIN (password)\" result=\"%.*s\"",
- s->id, ss_to_text(&s->ss), s->hostname, n, buf);
+ s->id, n, buf);
}
else {
strnvis(tmp, s->cmd, sizeof tmp, VIS_SAFE | VIS_CSTYLE);
log_info("%016"PRIx64" smtp "
- "failed-command address=%s host=%s command=\"%s\" "
+ "failed-command command=\"%s\" "
"result=\"%.*s\"",
- s->id, ss_to_text(&s->ss), s->hostname, tmp, n, buf);
+ s->id, tmp, n, buf);
}
break;
}
@@ -2339,9 +2353,9 @@ smtp_cert_init_cb(void *arg, int status, const char *name, const void *cert,
tree_pop(&wait_ssl_init, s->id);
if (status == CA_FAIL) {
- log_info("%016"PRIx64" smtp disconnected address=%s host=%s "
+ log_info("%016"PRIx64" smtp disconnected "
"reason=ca-failure",
- s->id, ss_to_text(&s->ss), s->hostname);
+ s->id);
smtp_free(s, "CA failure");
return;
}
@@ -2404,8 +2418,8 @@ smtp_cert_verify_cb(void *arg, int status)
log_debug("smtp: %p: smtp_cert_verify_cb: %s", s, reason);
if (!(s->flags & SF_VERIFIED) && (s->listener->flags & F_TLS_VERIFY)) {
- log_info("%016"PRIx64" smtp disconnected address=%s host=%s "
- " reason=%s", s->id, ss_to_text(&s->ss), s->hostname,
+ log_info("%016"PRIx64" smtp disconnected "
+ " reason=%s", s->id,
reason);
smtp_free(s, "SSL certificate check failed");
return;
@@ -2869,7 +2883,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/smtpd/smtpd.conf.5 b/smtpd/smtpd.conf.5
index cb776224..8736d3db 100644
--- a/smtpd/smtpd.conf.5
+++ b/smtpd/smtpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: smtpd.conf.5,v 1.207 2018/12/12 20:21:04 jmc Exp $
+.\" $OpenBSD: smtpd.conf.5,v 1.208 2018/12/21 21:35:29 gilles Exp $
.\"
.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org>
.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
@@ -17,7 +17,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.\"
-.Dd $Mdocdate: December 12 2018 $
+.Dd $Mdocdate: December 21 2018 $
.Dt SMTPD.CONF 5
.Os
.Sh NAME
@@ -485,6 +485,13 @@ Specify that session may address the string or list table
.Ar domain .
.It Xo
.Op Ic \&!
+.Cm for domain regex
+.Ar domain | Pf < Ar domain Ns >
+.Xc
+Specify that session may address the regex or regex table
+.Ar domain .
+.It Xo
+.Op Ic \&!
.Cm from any
.Xc
Specify that session may originate from any source.
@@ -508,6 +515,14 @@ Specify that session may only originate from the local enqueuer.
Specify that session may only originate from string or list table
.Ar address
which can be a specific address or a subnet expressed in CIDR-notation.
+.It Xo
+.Op Ic \&!
+.Cm from src regex
+.Ar address | Pf < Ar address Ns >
+.Xc
+Specify that session may only originate from regex or regex table
+.Ar address
+which can be a specific address or a subnet expressed in CIDR-notation.
.El
.Pp
In addition, the following transaction options:
@@ -526,6 +541,13 @@ Specify that session's HELO / EHLO should match the string or list table
.Ar helo-name .
.It Xo
.Op Ic \&!
+.Cm helo regex
+.Ar helo-name | Pf < Ar helo-name Ns >
+.Xc
+Specify that session's HELO / EHLO should match the regex or regex table
+.Ar helo-name .
+.It Xo
+.Op Ic \&!
.Cm mail\-from
.Ar sender | Pf < Ar sender Ns >
.Xc
@@ -533,6 +555,13 @@ Specify that transactions's MAIL FROM should match the string or list table
.Ar sender .
.It Xo
.Op Ic \&!
+.Cm mail\-from regex
+.Ar sender | Pf < Ar sender Ns >
+.Xc
+Specify that transactions's MAIL FROM should match the regex or regex table
+.Ar sender .
+.It Xo
+.Op Ic \&!
.Cm rcpt\-to
.Ar recipient | Pf < Ar recipient Ns >
.Xc
@@ -540,12 +569,26 @@ Specify that transaction's RCPT TO should match the string or list table
.Ar recipient .
.It Xo
.Op Ic \&!
+.Cm rcpt\-to regex
+.Ar recipient | Pf < Ar recipient Ns >
+.Xc
+Specify that transaction's RCPT TO should match the regex or regex table
+.Ar recipient .
+.It Xo
+.Op Ic \&!
.Cm tag Ar tag
.Xc
Matches transactions tagged with the given
.Ar tag .
.It Xo
.Op Ic \&!
+.Cm tag regex Ar tag
+.Xc
+Matches transactions tagged with the given
+.Ar tag
+regex .
+.It Xo
+.Op Ic \&!
.Cm tls
.Xc
Specify that transaction should take place in a TLS channel.
diff --git a/smtpd/smtpd.h b/smtpd/smtpd.h
index 18eba010..fafe7194 100644
--- a/smtpd/smtpd.h
+++ b/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.597 2018/12/21 21:35:29 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -436,7 +436,7 @@ enum expand_type {
};
enum filter_phase {
- FILTER_CONNECTED = 0,
+ FILTER_CONNECT,
FILTER_HELO,
FILTER_EHLO,
FILTER_STARTTLS,
@@ -552,6 +552,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];
@@ -608,8 +609,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
@@ -626,8 +625,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;
@@ -1065,26 +1065,43 @@ 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;
- int8_t not_table;
- struct table *table;
+ const char **chain;
+ size_t chain_size;
+ struct dict chain_procs;
- int8_t not_regex;
- struct table *regex;
+ int8_t not_fcrdns;
+ int8_t fcrdns;
int8_t not_rdns;
int8_t rdns;
- int8_t not_fcrdns;
- int8_t fcrdns;
+ int8_t not_rdns_table;
+ struct table *rdns_table;
+
+ int8_t not_rdns_regex;
+ struct table *rdns_regex;
+
+ int8_t not_src_table;
+ struct table *src_table;
+
+ int8_t not_src_regex;
+ struct table *src_regex;
+
};
enum filter_status {
@@ -1212,12 +1229,22 @@ struct rule {
int8_t flag_for;
int8_t flag_from_socket;
+ int8_t flag_tag_regex;
+ int8_t flag_for_regex;
+ int8_t flag_from_regex;
+
int8_t flag_smtp_helo;
int8_t flag_smtp_starttls;
int8_t flag_smtp_auth;
int8_t flag_smtp_mail_from;
int8_t flag_smtp_rcpt_to;
+ int8_t flag_smtp_helo_regex;
+ int8_t flag_smtp_starttls_regex;
+ int8_t flag_smtp_auth_regex;
+ int8_t flag_smtp_mail_from_regex;
+ int8_t flag_smtp_rcpt_to_regex;
+
char *table_tag;
char *table_from;
@@ -1348,11 +1375,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);
@@ -1372,7 +1402,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);
diff --git a/smtpd/ssl.c b/smtpd/ssl.c
index b88360eb..74932247 100644
--- a/smtpd/ssl.c
+++ b/smtpd/ssl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl.c,v 1.89 2017/05/17 14:00:06 deraadt Exp $ */
+/* $OpenBSD: ssl.c,v 1.90 2018/12/20 19:40:13 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -330,7 +330,7 @@ ssl_to_text(const SSL *ssl)
{
static char buf[256];
- (void)snprintf(buf, sizeof buf, "version=%s, cipher=%s, bits=%d",
+ (void)snprintf(buf, sizeof buf, "%s:%s:%d",
SSL_get_version(ssl),
SSL_get_cipher_name(ssl),
SSL_get_cipher_bits(ssl, NULL));
diff --git a/smtpd/table.c b/smtpd/table.c
index fc04c4ce..37249abd 100644
--- a/smtpd/table.c
+++ b/smtpd/table.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: table.c,v 1.32 2018/11/02 13:45:59 gilles Exp $ */
+/* $OpenBSD: table.c,v 1.33 2018/12/21 21:35:29 gilles Exp $ */
/*
* Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
@@ -471,8 +471,14 @@ int
table_regex_match(const char *string, const char *pattern)
{
regex_t preg;
+ int cflags = REG_EXTENDED|REG_NOSUB;
- if (regcomp(&preg, pattern, REG_EXTENDED|REG_NOSUB) != 0)
+ if (strncmp(pattern, "(?i)", 4) == 0) {
+ cflags |= REG_ICASE;
+ pattern += 4;
+ }
+
+ if (regcomp(&preg, pattern, cflags) != 0)
return (0);
if (regexec(&preg, string, 0, NULL, 0) != 0)