diff options
author | Gilles Chehade <gilles@poolp.org> | 2018-12-22 13:39:50 +0100 |
---|---|---|
committer | Gilles Chehade <gilles@poolp.org> | 2018-12-22 13:39:50 +0100 |
commit | 598cbe481d499dc277d4db7de7dd79ad476e2ad4 (patch) | |
tree | da265f75bfc5a0c7f76a8cd44d485b993c2685fb | |
parent | Merge branch 'master' into portable (diff) | |
parent | sync (diff) | |
download | OpenSMTPD-598cbe481d499dc277d4db7de7dd79ad476e2ad4.tar.xz OpenSMTPD-598cbe481d499dc277d4db7de7dd79ad476e2ad4.zip |
Merge branch 'master' into portable
-rw-r--r-- | smtpd/lka_filter.c | 287 | ||||
-rw-r--r-- | smtpd/parse.y | 62 | ||||
-rw-r--r-- | smtpd/smtpd.h | 14 |
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 { |