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