aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGilles Chehade <gilles@poolp.org>2018-12-22 13:39:50 +0100
committerGilles Chehade <gilles@poolp.org>2018-12-22 13:39:50 +0100
commit598cbe481d499dc277d4db7de7dd79ad476e2ad4 (patch)
treeda265f75bfc5a0c7f76a8cd44d485b993c2685fb
parentMerge branch 'master' into portable (diff)
parentsync (diff)
downloadOpenSMTPD-598cbe481d499dc277d4db7de7dd79ad476e2ad4.tar.xz
OpenSMTPD-598cbe481d499dc277d4db7de7dd79ad476e2ad4.zip
Merge branch 'master' into portable
-rw-r--r--smtpd/lka_filter.c287
-rw-r--r--smtpd/parse.y62
-rw-r--r--smtpd/smtpd.h14
3 files changed, 253 insertions, 110 deletions
diff --git a/smtpd/lka_filter.c b/smtpd/lka_filter.c
index f33962db..16c8f2ea 100644
--- a/smtpd/lka_filter.c
+++ b/smtpd/lka_filter.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka_filter.c,v 1.21 2018/12/21 20:38:42 gilles Exp $ */
+/* $OpenBSD: lka_filter.c,v 1.27 2018/12/22 12:31:40 gilles Exp $ */
/*
* Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
@@ -41,10 +41,12 @@
struct filter;
struct filter_session;
+static void filter_protocol_internal(struct filter_session *, uint64_t *, uint64_t, enum filter_phase, const char *);
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_next(uint64_t, uint64_t, enum filter_phase, const char *);
static void filter_protocol_query(struct filter *, uint64_t, uint64_t, const char *, const char *);
+static void filter_data_internal(struct filter_session *, uint64_t, 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 *);
@@ -74,6 +76,9 @@ struct filter_session {
char *rdns;
int fcrdns;
+ char *helo;
+ char *mail_from;
+
enum filter_phase phase;
};
@@ -139,6 +144,7 @@ lka_filter_init(void)
dict_init(&filters);
dict_init(&filter_chains);
+ /* first pass, allocate and init individual filters */
iter = NULL;
while (dict_iter(env->sc_filters_dict, &iter, &name, (void **)&filter_config)) {
switch (filter_config->filter_type) {
@@ -163,6 +169,7 @@ lka_filter_init(void)
}
}
+ /* second pass, allocate and init filter chains but don't build yet */
iter = NULL;
while (dict_iter(env->sc_filters_dict, &iter, &name, (void **)&filter_config)) {
switch (filter_config->filter_type) {
@@ -224,6 +231,7 @@ lka_filter_ready(void)
size_t i;
size_t j;
+ /* all filters are ready, actually build the filter chains */
iter = NULL;
while (dict_iter(&filters, &iter, &filter_name, (void **)&filter)) {
filter_chain = xcalloc(1, sizeof *filter_chain);
@@ -394,6 +402,7 @@ lka_filter_process_response(const char *name, const char *line)
char *ep = NULL;
char *kind = NULL;
char *qid = NULL;
+ /*char *phase = NULL;*/
char *response = NULL;
char *parameter = NULL;
@@ -472,7 +481,7 @@ lka_filter_process_response(const char *name, const char *line)
return 1;
}
- filter_protocol_next(token, reqid, parameter);
+ filter_protocol_next(token, reqid, 0, parameter);
return 1;
}
@@ -482,154 +491,168 @@ lka_filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param)
filter_protocol(reqid, phase, param);
}
-void
-filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param)
+static void
+filter_protocol_internal(struct filter_session *fs, uint64_t *token, 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);
+ if (!*token)
+ fs->phase = phase;
- 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;
-
- 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 */
- }
+ /* XXX - this sanity check requires a protocol change, stub for now */
+ phase = fs->phase;
+ if (fs->phase != phase)
+ fatalx("misbehaving filter");
- 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_result_reject(reqid, filter->config->reject);
- return;
- }
+ /* based on token, identify the filter_entry we should apply */
+ filter_chain = dict_get(&filter_chains, fs->filter_name);
+ filter_entry = TAILQ_FIRST(&filter_chain->chain[fs->phase]);
+ if (*token) {
+ TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries)
+ if (filter_entry->id == *token)
+ break;
+ if (filter_entry == NULL)
+ fatalx("misbehaving filter");
+ filter_entry = TAILQ_NEXT(filter_entry, entries);
}
-proceed:
- 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)
+ /* no filter_entry, we either had none or reached end of chain */
+ if (filter_entry == NULL) {
+ filter_result_proceed(reqid);
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 */
- }
+ /* process param with current filter_entry */
+ *token = filter_entry->id;
+ 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 response */
+ }
- 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);
+ if (filter_execs[fs->phase].func(fs, filter, reqid, param)) {
+ if (filter->config->rewrite) {
+ filter_result_rewrite(reqid, filter->config->rewrite);
+ return;
+ }
+ else if (filter->config->disconnect) {
+ filter_result_disconnect(reqid, filter->config->disconnect);
+ return;
+ }
+ else {
+ filter_result_reject(reqid, filter->config->reject);
return;
}
}
- filter_result_proceed(reqid);
+ /* filter_entry resulted in proceed, try next filter */
+ filter_protocol_internal(fs, token, reqid, phase, param);
+ return;
}
-
static void
-filter_data(uint64_t reqid, const char *line)
+filter_data_internal(struct filter_session *fs, uint64_t token, uint64_t reqid, const char *line)
{
- struct filter_session *fs;
struct filter_chain *filter_chain;
struct filter_entry *filter_entry;
struct filter *filter;
- fs = tree_xget(&sessions, reqid);
+ if (!token)
+ fs->phase = FILTER_DATA_LINE;
+ if (fs->phase != FILTER_DATA_LINE)
+ fatalx("misbehaving filter");
- fs->phase = FILTER_DATA_LINE;
+ /* based on token, identify the filter_entry we should apply */
filter_chain = dict_get(&filter_chains, fs->filter_name);
filter_entry = TAILQ_FIRST(&filter_chain->chain[fs->phase]);
+ if (token) {
+ TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries)
+ if (filter_entry->id == token)
+ break;
+ if (filter_entry == NULL)
+ fatalx("misbehaving filter");
+ filter_entry = TAILQ_NEXT(filter_entry, entries);
+ }
+
+ /* no filter_entry, we either had none or reached end of chain */
if (filter_entry == NULL) {
io_printf(fs->io, "%s\r\n", line);
return;
}
+ /* pass data to the filter */
filter = dict_get(&filters, filter_entry->name);
filter_data_query(filter, filter_entry->id, reqid, line);
}
static void
-filter_data_next(uint64_t token, uint64_t reqid, const char *line)
+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;
+ struct filter_session *fs;
+ uint64_t token = 0;
- /* client session may have disappeared while we were in proc */
- if ((fs = tree_get(&sessions, reqid)) == NULL)
- return;
+ fs = tree_xget(&sessions, reqid);
- filter_chain = dict_get(&filter_chains, fs->filter_name);
+ switch (phase) {
+ case FILTER_HELO:
+ case FILTER_EHLO:
+ if (fs->helo)
+ free(fs->helo);
+ fs->helo = xstrdup(param);
+ break;
+ case FILTER_MAIL_FROM:
+ if (fs->mail_from)
+ free(fs->mail_from);
- TAILQ_FOREACH(filter_entry, &filter_chain->chain[fs->phase], entries)
- if (filter_entry->id == token)
- break;
+ fs->mail_from = xstrdup(param + 1);
+ *strchr(fs->mail_from, '>') = '\0';
- 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;
+ break;
+ case FILTER_STARTTLS:
+ case FILTER_AUTH:
+ /* TBD */
+ break;
+ default:
+ break;
}
+ filter_protocol_internal(fs, &token, reqid, phase, param);
+}
+
+static void
+filter_protocol_next(uint64_t token, uint64_t reqid, enum filter_phase phase, const char *param)
+{
+ struct filter_session *fs;
- io_printf(fs->io, "%s\r\n", line);
+ /* session can legitimately disappear on a resume */
+ if ((fs = tree_get(&sessions, reqid)) == NULL)
+ return;
+
+ filter_protocol_internal(fs, &token, reqid, phase, param);
}
+static void
+filter_data(uint64_t reqid, const char *line)
+{
+ struct filter_session *fs;
-int
-lka_filter_response(uint64_t reqid, const char *response, const char *param)
+ fs = tree_xget(&sessions, reqid);
+
+ filter_data_internal(fs, 0, reqid, line);
+}
+
+static void
+filter_data_next(uint64_t token, uint64_t reqid, const char *line)
{
- if (strcmp(response, "proceed") == 0)
- filter_result_proceed(reqid);
- else if (strcmp(response, "rewrite") == 0)
- filter_result_rewrite(reqid, param);
- else if (strcmp(response, "reject") == 0)
- filter_result_reject(reqid, param);
- else if (strcmp(response, "disconnect") == 0)
- filter_result_disconnect(reqid, param);
- else
- return 0;
- return 1;
+ struct filter_session *fs;
+
+ /* session can legitimately disappear on a resume */
+ if ((fs = tree_get(&sessions, reqid)) == NULL)
+ return;
+
+ filter_data_internal(fs, token, reqid, line);
}
static void
@@ -768,6 +791,58 @@ filter_check_src_regex(struct filter *filter, const char *key)
}
static int
+filter_check_helo_table(struct filter *filter, enum table_service kind, const char *key)
+{
+ int ret = 0;
+
+ if (filter->config->helo_table) {
+ if (table_lookup(filter->config->helo_table, NULL, key, kind, NULL) > 0)
+ ret = 1;
+ ret = filter->config->not_helo_table < 0 ? !ret : ret;
+ }
+ return ret;
+}
+
+static int
+filter_check_helo_regex(struct filter *filter, const char *key)
+{
+ int ret = 0;
+
+ if (filter->config->helo_regex) {
+ if (table_lookup(filter->config->helo_regex, NULL, key, K_REGEX, NULL) > 0)
+ ret = 1;
+ ret = filter->config->not_helo_regex < 0 ? !ret : ret;
+ }
+ return ret;
+}
+
+static int
+filter_check_mail_from_table(struct filter *filter, enum table_service kind, const char *key)
+{
+ int ret = 0;
+
+ if (filter->config->mail_from_table) {
+ if (table_lookup(filter->config->mail_from_table, NULL, key, kind, NULL) > 0)
+ ret = 1;
+ ret = filter->config->not_mail_from_table < 0 ? !ret : ret;
+ }
+ return ret;
+}
+
+static int
+filter_check_mail_from_regex(struct filter *filter, const char *key)
+{
+ int ret = 0;
+
+ if (filter->config->mail_from_regex) {
+ if (table_lookup(filter->config->mail_from_regex, NULL, key, K_REGEX, NULL) > 0)
+ ret = 1;
+ ret = filter->config->not_mail_from_regex < 0 ? !ret : ret;
+ }
+ return ret;
+}
+
+static int
filter_check_fcrdns(struct filter *filter, int fcrdns)
{
int ret = 0;
@@ -809,7 +884,11 @@ filter_builtins_global(struct filter_session *fs, struct filter *filter, uint64_
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)))
+ filter_check_src_regex(filter, ss_to_text(&fs->ss_src)) ||
+ filter_check_helo_table(filter, K_DOMAIN, fs->helo) ||
+ filter_check_helo_regex(filter, fs->helo) ||
+ filter_check_mail_from_table(filter, K_MAILADDR, fs->mail_from) ||
+ filter_check_mail_from_regex(filter, fs->mail_from))
return 1;
return 0;
}
diff --git a/smtpd/parse.y b/smtpd/parse.y
index 6d8b1cde..fae944a0 100644
--- a/smtpd/parse.y
+++ b/smtpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.243 2018/12/22 09:30:19 gilles Exp $ */
+/* $OpenBSD: parse.y,v 1.245 2018/12/22 12:31:40 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -1336,6 +1336,32 @@ negation SRC REGEX tables {
}
;
+filter_phase_check_helo_table:
+negation HELO tables {
+ filter_config->not_helo_table = $1 ? -1 : 1;
+ filter_config->helo_table = $3;
+}
+;
+filter_phase_check_helo_regex:
+negation HELO REGEX tables {
+ filter_config->not_helo_regex = $1 ? -1 : 1;
+ filter_config->helo_regex = $4;
+}
+;
+
+filter_phase_check_mail_from_table:
+negation MAIL_FROM tables {
+ filter_config->not_mail_from_table = $1 ? -1 : 1;
+ filter_config->mail_from_table = $3;
+}
+;
+filter_phase_check_mail_from_regex:
+negation MAIL_FROM REGEX tables {
+ filter_config->not_mail_from_regex = $1 ? -1 : 1;
+ filter_config->mail_from_regex = $4;
+}
+;
+
filter_phase_global_options:
filter_phase_check_fcrdns |
filter_phase_check_rdns |
@@ -1348,27 +1374,53 @@ filter_phase_connect_options:
filter_phase_global_options;
filter_phase_helo_options:
+filter_phase_check_helo_table |
+filter_phase_check_helo_regex |
filter_phase_global_options;
filter_phase_mail_from_options:
+filter_phase_check_helo_table |
+filter_phase_check_helo_regex |
+filter_phase_check_mail_from_table |
+filter_phase_check_mail_from_regex |
filter_phase_global_options;
filter_phase_rcpt_to_options:
+filter_phase_check_helo_table |
+filter_phase_check_helo_regex |
+filter_phase_check_mail_from_table |
+filter_phase_check_mail_from_regex |
filter_phase_global_options;
filter_phase_data_options:
+filter_phase_check_helo_table |
+filter_phase_check_helo_regex |
+filter_phase_check_mail_from_table |
+filter_phase_check_mail_from_regex |
filter_phase_global_options;
+/*
filter_phase_quit_options:
+filter_phase_check_helo_table |
+filter_phase_check_helo_regex |
filter_phase_global_options;
filter_phase_rset_options:
+filter_phase_check_helo_table |
+filter_phase_check_helo_regex |
filter_phase_global_options;
filter_phase_noop_options:
+filter_phase_check_helo_table |
+filter_phase_check_helo_regex |
filter_phase_global_options;
+*/
filter_phase_commit_options:
+filter_phase_check_helo_table |
+filter_phase_check_helo_regex |
+filter_phase_check_mail_from_table |
+filter_phase_check_mail_from_regex |
filter_phase_global_options;
@@ -1415,7 +1467,6 @@ DATA_LINE {
filter_config->phase = FILTER_DATA_LINE;
} filter_action_builtin
;
-*/
filter_phase_quit:
QUIT {
@@ -1434,6 +1485,7 @@ NOOP {
filter_config->phase = FILTER_NOOP;
} filter_phase_noop_options filter_action_builtin
;
+*/
filter_phase_commit:
COMMIT {
@@ -1451,9 +1503,9 @@ filter_phase_connect
| filter_phase_rcpt_to
| filter_phase_data
/*| filter_phase_data_line*/
-| filter_phase_quit
-| filter_phase_noop
-| filter_phase_rset
+/*| filter_phase_quit*/
+/*| filter_phase_noop*/
+/*| filter_phase_rset*/
| filter_phase_commit
;
diff --git a/smtpd/smtpd.h b/smtpd/smtpd.h
index 141f8498..a5c704bf 100644
--- a/smtpd/smtpd.h
+++ b/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.598 2018/12/22 08:54:02 gilles Exp $ */
+/* $OpenBSD: smtpd.h,v 1.600 2018/12/22 12:31:40 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -1102,6 +1102,18 @@ struct filter_config {
int8_t not_src_regex;
struct table *src_regex;
+ int8_t not_helo_table;
+ struct table *helo_table;
+
+ int8_t not_helo_regex;
+ struct table *helo_regex;
+
+ int8_t not_mail_from_table;
+ struct table *mail_from_table;
+
+ int8_t not_mail_from_regex;
+ struct table *mail_from_regex;
+
};
enum filter_status {