xref: /openbsd/usr.sbin/smtpd/ruleset.c (revision d3140113)
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