1*d3140113Seric /* $OpenBSD: ruleset.c,v 1.48 2021/06/14 17:58:16 eric Exp $ */
2bcd6dbfbSgilles
3bcd6dbfbSgilles /*
465c4fdfbSgilles * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
5bcd6dbfbSgilles *
6bcd6dbfbSgilles * Permission to use, copy, modify, and distribute this software for any
7bcd6dbfbSgilles * purpose with or without fee is hereby granted, provided that the above
8bcd6dbfbSgilles * copyright notice and this permission notice appear in all copies.
9bcd6dbfbSgilles *
10bcd6dbfbSgilles * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11bcd6dbfbSgilles * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12bcd6dbfbSgilles * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13bcd6dbfbSgilles * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14bcd6dbfbSgilles * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15bcd6dbfbSgilles * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16bcd6dbfbSgilles * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17bcd6dbfbSgilles */
18bcd6dbfbSgilles
195875f871Seric #include <errno.h>
20bcd6dbfbSgilles #include <string.h>
21bcd6dbfbSgilles
22bcd6dbfbSgilles #include "smtpd.h"
23bcd6dbfbSgilles
248ff50274Seric #define MATCH_RESULT(r, neg) ((r) == -1 ? -1 : ((neg) < 0 ? !(r) : (r)))
25be925435Sgilles
26a8e22235Sgilles static int
ruleset_match_tag(struct rule * r,const struct envelope * evp)27a8e22235Sgilles ruleset_match_tag(struct rule *r, const struct envelope *evp)
28a8e22235Sgilles {
29a8e22235Sgilles int ret;
30a8e22235Sgilles struct table *table;
314614b657Sgilles enum table_service service = K_STRING;
32a8e22235Sgilles
33a8e22235Sgilles if (!r->flag_tag)
34a8e22235Sgilles return 1;
35a8e22235Sgilles
364614b657Sgilles if (r->flag_tag_regex)
374614b657Sgilles service = K_REGEX;
384614b657Sgilles
39ff18143eSeric table = table_find(env, r->table_tag);
408ff50274Seric ret = table_match(table, service, evp->tag);
41a8e22235Sgilles
428ff50274Seric return MATCH_RESULT(ret, r->flag_tag);
43a8e22235Sgilles }
44a8e22235Sgilles
45a8e22235Sgilles static int
ruleset_match_from(struct rule * r,const struct envelope * evp)46a8e22235Sgilles ruleset_match_from(struct rule *r, const struct envelope *evp)
47a8e22235Sgilles {
48a8e22235Sgilles int ret;
49a9b72af9Sgilles int has_rdns;
50a8e22235Sgilles const char *key;
51a8e22235Sgilles struct table *table;
524614b657Sgilles enum table_service service = K_NETADDR;
53a8e22235Sgilles
54a8e22235Sgilles if (!r->flag_from)
55a8e22235Sgilles return 1;
56a8e22235Sgilles
57a847aabaSgilles if (evp->flags & EF_INTERNAL) {
58a847aabaSgilles /* if expanded from an empty table_from, skip rule
59a847aabaSgilles * if no table
60a847aabaSgilles */
61a847aabaSgilles if (r->table_from == NULL)
62a847aabaSgilles return 0;
63a8e22235Sgilles key = "local";
64a847aabaSgilles }
65c229687eSgilles else if (r->flag_from_rdns) {
66a9b72af9Sgilles has_rdns = strcmp(evp->hostname, "<unknown>") != 0;
67a9b72af9Sgilles if (r->table_from == NULL)
68a9b72af9Sgilles return MATCH_RESULT(has_rdns, r->flag_from);
69a9b72af9Sgilles if (!has_rdns)
70c229687eSgilles return 0;
71c229687eSgilles key = evp->hostname;
72c229687eSgilles }
7357bda1deSgilles else {
74a8e22235Sgilles key = ss_to_text(&evp->ss);
755845f8e6Sgilles if (r->flag_from_socket) {
7657bda1deSgilles if (strcmp(key, "local") == 0)
7757bda1deSgilles return MATCH_RESULT(1, r->flag_from);
785845f8e6Sgilles else
795845f8e6Sgilles return r->flag_from < 0 ? 1 : 0;
805845f8e6Sgilles }
8157bda1deSgilles }
824614b657Sgilles if (r->flag_from_regex)
834614b657Sgilles service = K_REGEX;
844614b657Sgilles
85ff18143eSeric table = table_find(env, r->table_from);
868ff50274Seric ret = table_match(table, service, key);
87a8e22235Sgilles
888ff50274Seric return MATCH_RESULT(ret, r->flag_from);
89a8e22235Sgilles }
90a8e22235Sgilles
91a8e22235Sgilles static int
ruleset_match_to(struct rule * r,const struct envelope * evp)92a8e22235Sgilles ruleset_match_to(struct rule *r, const struct envelope *evp)
93a8e22235Sgilles {
94a8e22235Sgilles int ret;
95a8e22235Sgilles struct table *table;
964614b657Sgilles enum table_service service = K_DOMAIN;
97a8e22235Sgilles
98a8e22235Sgilles if (!r->flag_for)
99a8e22235Sgilles return 1;
100a8e22235Sgilles
1014614b657Sgilles if (r->flag_for_regex)
1024614b657Sgilles service = K_REGEX;
1034614b657Sgilles
104ff18143eSeric table = table_find(env, r->table_for);
1058ff50274Seric ret = table_match(table, service, evp->dest.domain);
106a8e22235Sgilles
1078ff50274Seric return MATCH_RESULT(ret, r->flag_for);
108a8e22235Sgilles }
109a8e22235Sgilles
110a8e22235Sgilles static int
ruleset_match_smtp_helo(struct rule * r,const struct envelope * evp)111a8e22235Sgilles ruleset_match_smtp_helo(struct rule *r, const struct envelope *evp)
112a8e22235Sgilles {
113a8e22235Sgilles int ret;
114a8e22235Sgilles struct table *table;
1154614b657Sgilles enum table_service service = K_DOMAIN;
116a8e22235Sgilles
117a8e22235Sgilles if (!r->flag_smtp_helo)
118a8e22235Sgilles return 1;
119a8e22235Sgilles
1204614b657Sgilles if (r->flag_smtp_helo_regex)
1214614b657Sgilles service = K_REGEX;
1224614b657Sgilles
123ff18143eSeric table = table_find(env, r->table_smtp_helo);
1248ff50274Seric ret = table_match(table, service, evp->helo);
125a8e22235Sgilles
1268ff50274Seric return MATCH_RESULT(ret, r->flag_smtp_helo);
127a8e22235Sgilles }
128a8e22235Sgilles
129a8e22235Sgilles static int
ruleset_match_smtp_starttls(struct rule * r,const struct envelope * evp)130a8e22235Sgilles ruleset_match_smtp_starttls(struct rule *r, const struct envelope *evp)
131a8e22235Sgilles {
132a8e22235Sgilles if (!r->flag_smtp_starttls)
133a8e22235Sgilles return 1;
134a8e22235Sgilles
135a8e22235Sgilles /* XXX - not until TLS flag is added to envelope */
136a8e22235Sgilles return -1;
137a8e22235Sgilles }
138a8e22235Sgilles
139a8e22235Sgilles static int
ruleset_match_smtp_auth(struct rule * r,const struct envelope * evp)140a8e22235Sgilles ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp)
141a8e22235Sgilles {
142a8e22235Sgilles int ret;
1437d9f71f9Sgilles struct table *table;
1447d9f71f9Sgilles enum table_service service;
145a8e22235Sgilles
146a8e22235Sgilles if (!r->flag_smtp_auth)
147a8e22235Sgilles return 1;
148a8e22235Sgilles
149a8e22235Sgilles if (!(evp->flags & EF_AUTHENTICATED))
150a8e22235Sgilles ret = 0;
151a8e22235Sgilles else if (r->table_smtp_auth) {
152a8e22235Sgilles
1537d9f71f9Sgilles if (r->flag_smtp_auth_regex)
1547d9f71f9Sgilles service = K_REGEX;
1557d9f71f9Sgilles else
1567d9f71f9Sgilles service = strchr(evp->username, '@') ?
1577d9f71f9Sgilles K_MAILADDR : K_STRING;
1587d9f71f9Sgilles table = table_find(env, r->table_smtp_auth);
1597d9f71f9Sgilles ret = table_match(table, service, evp->username);
160a8e22235Sgilles }
161a8e22235Sgilles else
162a8e22235Sgilles ret = 1;
163a8e22235Sgilles
1648ff50274Seric return MATCH_RESULT(ret, r->flag_smtp_auth);
165a8e22235Sgilles }
166a8e22235Sgilles
167a8e22235Sgilles static int
ruleset_match_smtp_mail_from(struct rule * r,const struct envelope * evp)168a8e22235Sgilles ruleset_match_smtp_mail_from(struct rule *r, const struct envelope *evp)
169a8e22235Sgilles {
170a8e22235Sgilles int ret;
171a8e22235Sgilles const char *key;
172a8e22235Sgilles struct table *table;
1734614b657Sgilles enum table_service service = K_MAILADDR;
174a8e22235Sgilles
175a8e22235Sgilles if (!r->flag_smtp_mail_from)
176a8e22235Sgilles return 1;
177a8e22235Sgilles
1784614b657Sgilles if (r->flag_smtp_mail_from_regex)
1794614b657Sgilles service = K_REGEX;
1804614b657Sgilles
181a8e22235Sgilles if ((key = mailaddr_to_text(&evp->sender)) == NULL)
182a8e22235Sgilles return -1;
183a8e22235Sgilles
184ff18143eSeric table = table_find(env, r->table_smtp_mail_from);
1858ff50274Seric ret = table_match(table, service, key);
186a8e22235Sgilles
1878ff50274Seric return MATCH_RESULT(ret, r->flag_smtp_mail_from);
188a8e22235Sgilles }
189a8e22235Sgilles
190a8e22235Sgilles static int
ruleset_match_smtp_rcpt_to(struct rule * r,const struct envelope * evp)191a8e22235Sgilles ruleset_match_smtp_rcpt_to(struct rule *r, const struct envelope *evp)
192a8e22235Sgilles {
193a8e22235Sgilles int ret;
194a8e22235Sgilles const char *key;
195a8e22235Sgilles struct table *table;
1964614b657Sgilles enum table_service service = K_MAILADDR;
197a8e22235Sgilles
198a8e22235Sgilles if (!r->flag_smtp_rcpt_to)
199a8e22235Sgilles return 1;
200a8e22235Sgilles
2014614b657Sgilles if (r->flag_smtp_rcpt_to_regex)
2024614b657Sgilles service = K_REGEX;
2034614b657Sgilles
204a8e22235Sgilles if ((key = mailaddr_to_text(&evp->dest)) == NULL)
205a8e22235Sgilles return -1;
206a8e22235Sgilles
207ff18143eSeric table = table_find(env, r->table_smtp_rcpt_to);
2088ff50274Seric ret = table_match(table, service, key);
209a8e22235Sgilles
2108ff50274Seric return MATCH_RESULT(ret, r->flag_smtp_rcpt_to);
211a8e22235Sgilles }
212bcd6dbfbSgilles
213bcd6dbfbSgilles struct rule *
ruleset_match(const struct envelope * evp)2147791da2bSeric ruleset_match(const struct envelope *evp)
215bcd6dbfbSgilles {
216bcd6dbfbSgilles struct rule *r;
217a8e22235Sgilles int i = 0;
218bcd6dbfbSgilles
219a8e22235Sgilles #define MATCH_EVAL(x) \
220a8e22235Sgilles switch ((x)) { \
221a8e22235Sgilles case -1: goto tempfail; \
222a8e22235Sgilles case 0: continue; \
223a8e22235Sgilles default: break; \
224a8e22235Sgilles }
225bcd6dbfbSgilles TAILQ_FOREACH(r, env->sc_rules, r_entry) {
226a8e22235Sgilles ++i;
227a8e22235Sgilles MATCH_EVAL(ruleset_match_tag(r, evp));
228a8e22235Sgilles MATCH_EVAL(ruleset_match_from(r, evp));
229a8e22235Sgilles MATCH_EVAL(ruleset_match_to(r, evp));
230a8e22235Sgilles MATCH_EVAL(ruleset_match_smtp_helo(r, evp));
231a8e22235Sgilles MATCH_EVAL(ruleset_match_smtp_auth(r, evp));
232a8e22235Sgilles MATCH_EVAL(ruleset_match_smtp_starttls(r, evp));
233a8e22235Sgilles MATCH_EVAL(ruleset_match_smtp_mail_from(r, evp));
234a8e22235Sgilles MATCH_EVAL(ruleset_match_smtp_rcpt_to(r, evp));
23565c4fdfbSgilles goto matched;
2368c175df0Sgilles }
237a8e22235Sgilles #undef MATCH_EVAL
238bcd6dbfbSgilles
2395875f871Seric errno = 0;
240cc81b7c6Seric log_trace(TRACE_RULES, "no rule matched");
2415875f871Seric return (NULL);
24265c4fdfbSgilles
243a8e22235Sgilles tempfail:
244a8e22235Sgilles errno = EAGAIN;
245a8e22235Sgilles log_trace(TRACE_RULES, "temporary failure in processing of a rule");
246a8e22235Sgilles return (NULL);
247a8e22235Sgilles
24865c4fdfbSgilles matched:
249a8e22235Sgilles log_trace(TRACE_RULES, "rule #%d matched: %s", i, rule_to_text(r));
25065c4fdfbSgilles return r;
251bcd6dbfbSgilles }
252