diff options
author | Gilles Chehade <gilles@poolp.org> | 2020-04-30 02:02:03 +0200 |
---|---|---|
committer | Gilles Chehade <gilles@poolp.org> | 2020-04-30 02:02:03 +0200 |
commit | 9db3604d31a8d7bd43a3c8184a7dc53fca7940a2 (patch) | |
tree | 498b190740f593d279d0b4ce6e4f6d5333748874 /ruleset.c | |
parent | move (diff) | |
parent | In virtual alias context, do not try to match catchall entries for (diff) | |
download | OpenSMTPD-9db3604d31a8d7bd43a3c8184a7dc53fca7940a2.tar.xz OpenSMTPD-9db3604d31a8d7bd43a3c8184a7dc53fca7940a2.zip |
Merge remote-tracking branch 'portable/native'
Diffstat (limited to 'ruleset.c')
-rw-r--r-- | ruleset.c | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/ruleset.c b/ruleset.c new file mode 100644 index 00000000..869d8115 --- /dev/null +++ b/ruleset.c @@ -0,0 +1,263 @@ +/* $OpenBSD: ruleset.c,v 1.47 2019/11/25 14:18:33 gilles Exp $ */ + +/* + * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/tree.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <errno.h> +#include <event.h> +#include <imsg.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> + +#include "smtpd.h" +#include "log.h" + +#define MATCH_RESULT(r, neg) ((r) == -1 ? -1 : ((neg) < 0 ? !(r) : (r))) + +static int +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); + ret = table_match(table, service, evp->tag); + + return MATCH_RESULT(ret, r->flag_tag); +} + +static int +ruleset_match_from(struct rule *r, const struct envelope *evp) +{ + int ret; + int has_rdns; + const char *key; + struct table *table; + enum table_service service = K_NETADDR; + + if (!r->flag_from) + return 1; + + if (evp->flags & EF_INTERNAL) { + /* if expanded from an empty table_from, skip rule + * if no table + */ + if (r->table_from == NULL) + return 0; + key = "local"; + } + else if (r->flag_from_rdns) { + has_rdns = strcmp(evp->hostname, "<unknown>") != 0; + if (r->table_from == NULL) + return MATCH_RESULT(has_rdns, r->flag_from); + if (!has_rdns) + return 0; + key = evp->hostname; + } + else { + key = ss_to_text(&evp->ss); + if (r->flag_from_socket) { + if (strcmp(key, "local") == 0) + return MATCH_RESULT(1, r->flag_from); + else + return r->flag_from < 0 ? 1 : 0; + } + } + if (r->flag_from_regex) + service = K_REGEX; + + table = table_find(env, r->table_from); + ret = table_match(table, service, key); + + return MATCH_RESULT(ret, r->flag_from); +} + +static int +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); + ret = table_match(table, service, evp->dest.domain); + + return MATCH_RESULT(ret, r->flag_for); +} + +static int +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); + ret = table_match(table, service, evp->helo); + + return MATCH_RESULT(ret, r->flag_smtp_helo); +} + +static int +ruleset_match_smtp_starttls(struct rule *r, const struct envelope *evp) +{ + if (!r->flag_smtp_starttls) + return 1; + + /* XXX - not until TLS flag is added to envelope */ + return -1; +} + +static int +ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp) +{ + int ret; + struct table *table; + enum table_service service; + + if (!r->flag_smtp_auth) + return 1; + + if (!(evp->flags & EF_AUTHENTICATED)) + ret = 0; + else if (r->table_smtp_auth) { + + if (r->flag_smtp_auth_regex) + service = K_REGEX; + else + service = strchr(evp->username, '@') ? + K_MAILADDR : K_STRING; + table = table_find(env, r->table_smtp_auth); + ret = table_match(table, service, evp->username); + } + else + ret = 1; + + return MATCH_RESULT(ret, r->flag_smtp_auth); +} + +static int +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); + ret = table_match(table, service, key); + + return MATCH_RESULT(ret, r->flag_smtp_mail_from); +} + +static int +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); + ret = table_match(table, service, key); + + return MATCH_RESULT(ret, r->flag_smtp_rcpt_to); +} + +struct rule * +ruleset_match(const struct envelope *evp) +{ + struct rule *r; + int i = 0; + +#define MATCH_EVAL(x) \ + switch ((x)) { \ + case -1: goto tempfail; \ + case 0: continue; \ + default: break; \ + } + TAILQ_FOREACH(r, env->sc_rules, r_entry) { + ++i; + MATCH_EVAL(ruleset_match_tag(r, evp)); + MATCH_EVAL(ruleset_match_from(r, evp)); + MATCH_EVAL(ruleset_match_to(r, evp)); + MATCH_EVAL(ruleset_match_smtp_helo(r, evp)); + MATCH_EVAL(ruleset_match_smtp_auth(r, evp)); + MATCH_EVAL(ruleset_match_smtp_starttls(r, evp)); + MATCH_EVAL(ruleset_match_smtp_mail_from(r, evp)); + MATCH_EVAL(ruleset_match_smtp_rcpt_to(r, evp)); + goto matched; + } +#undef MATCH_EVAL + + errno = 0; + log_trace(TRACE_RULES, "no rule matched"); + return (NULL); + +tempfail: + errno = EAGAIN; + log_trace(TRACE_RULES, "temporary failure in processing of a rule"); + return (NULL); + +matched: + log_trace(TRACE_RULES, "rule #%d matched: %s", i, rule_to_text(r)); + return r; +} |