xref: /openbsd/usr.sbin/smtpd/ruleset.c (revision d3140113)
1 /*	$OpenBSD: ruleset.c,v 1.48 2021/06/14 17:58:16 eric Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <errno.h>
20 #include <string.h>
21 
22 #include "smtpd.h"
23 
24 #define MATCH_RESULT(r, neg) ((r) == -1 ? -1 : ((neg) < 0 ? !(r) : (r)))
25 
26 static int
ruleset_match_tag(struct rule * r,const struct envelope * evp)27 ruleset_match_tag(struct rule *r, const struct envelope *evp)
28 {
29 	int		ret;
30 	struct table	*table;
31 	enum table_service service = K_STRING;
32 
33 	if (!r->flag_tag)
34 		return 1;
35 
36 	if (r->flag_tag_regex)
37 		service = K_REGEX;
38 
39 	table = table_find(env, r->table_tag);
40 	ret = table_match(table, service, evp->tag);
41 
42 	return MATCH_RESULT(ret, r->flag_tag);
43 }
44 
45 static int
ruleset_match_from(struct rule * r,const struct envelope * evp)46 ruleset_match_from(struct rule *r, const struct envelope *evp)
47 {
48 	int		ret;
49 	int		has_rdns;
50 	const char	*key;
51 	struct table	*table;
52 	enum table_service service = K_NETADDR;
53 
54 	if (!r->flag_from)
55 		return 1;
56 
57 	if (evp->flags & EF_INTERNAL) {
58 		/* if expanded from an empty table_from, skip rule
59 		 * if no table
60 		 */
61 		if (r->table_from == NULL)
62 			return 0;
63 		key = "local";
64 	}
65 	else if (r->flag_from_rdns) {
66 		has_rdns = strcmp(evp->hostname, "<unknown>") != 0;
67 		if (r->table_from == NULL)
68 		  	return MATCH_RESULT(has_rdns, r->flag_from);
69 		if (!has_rdns)
70 			return 0;
71 		key = evp->hostname;
72 	}
73 	else {
74 		key = ss_to_text(&evp->ss);
75 		if (r->flag_from_socket) {
76 			if (strcmp(key, "local") == 0)
77 				return MATCH_RESULT(1, r->flag_from);
78 			else
79 				return r->flag_from < 0 ? 1 : 0;
80 		}
81 	}
82 	if (r->flag_from_regex)
83 		service = K_REGEX;
84 
85 	table = table_find(env, r->table_from);
86 	ret = table_match(table, service, key);
87 
88 	return MATCH_RESULT(ret, r->flag_from);
89 }
90 
91 static int
ruleset_match_to(struct rule * r,const struct envelope * evp)92 ruleset_match_to(struct rule *r, const struct envelope *evp)
93 {
94 	int		ret;
95 	struct table	*table;
96 	enum table_service service = K_DOMAIN;
97 
98 	if (!r->flag_for)
99 		return 1;
100 
101 	if (r->flag_for_regex)
102 		service = K_REGEX;
103 
104 	table = table_find(env, r->table_for);
105 	ret = table_match(table, service, evp->dest.domain);
106 
107 	return MATCH_RESULT(ret, r->flag_for);
108 }
109 
110 static int
ruleset_match_smtp_helo(struct rule * r,const struct envelope * evp)111 ruleset_match_smtp_helo(struct rule *r, const struct envelope *evp)
112 {
113 	int		ret;
114 	struct table	*table;
115 	enum table_service service = K_DOMAIN;
116 
117 	if (!r->flag_smtp_helo)
118 		return 1;
119 
120 	if (r->flag_smtp_helo_regex)
121 		service = K_REGEX;
122 
123 	table = table_find(env, r->table_smtp_helo);
124 	ret = table_match(table, service, evp->helo);
125 
126 	return MATCH_RESULT(ret, r->flag_smtp_helo);
127 }
128 
129 static int
ruleset_match_smtp_starttls(struct rule * r,const struct envelope * evp)130 ruleset_match_smtp_starttls(struct rule *r, const struct envelope *evp)
131 {
132 	if (!r->flag_smtp_starttls)
133 		return 1;
134 
135 	/* XXX - not until TLS flag is added to envelope */
136 	return -1;
137 }
138 
139 static int
ruleset_match_smtp_auth(struct rule * r,const struct envelope * evp)140 ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp)
141 {
142 	int	ret;
143 	struct table	*table;
144 	enum table_service service;
145 
146 	if (!r->flag_smtp_auth)
147 		return 1;
148 
149 	if (!(evp->flags & EF_AUTHENTICATED))
150 		ret = 0;
151 	else if (r->table_smtp_auth) {
152 
153 		if (r->flag_smtp_auth_regex)
154 			service = K_REGEX;
155 		else
156 			service = strchr(evp->username, '@') ?
157 				K_MAILADDR : K_STRING;
158 		table = table_find(env, r->table_smtp_auth);
159 		ret = table_match(table, service, evp->username);
160 	}
161 	else
162 		ret = 1;
163 
164 	return MATCH_RESULT(ret, r->flag_smtp_auth);
165 }
166 
167 static int
ruleset_match_smtp_mail_from(struct rule * r,const struct envelope * evp)168 ruleset_match_smtp_mail_from(struct rule *r, const struct envelope *evp)
169 {
170 	int		ret;
171 	const char	*key;
172 	struct table	*table;
173 	enum table_service service = K_MAILADDR;
174 
175 	if (!r->flag_smtp_mail_from)
176 		return 1;
177 
178 	if (r->flag_smtp_mail_from_regex)
179 		service = K_REGEX;
180 
181 	if ((key = mailaddr_to_text(&evp->sender)) == NULL)
182 		return -1;
183 
184 	table = table_find(env, r->table_smtp_mail_from);
185 	ret = table_match(table, service, key);
186 
187 	return MATCH_RESULT(ret, r->flag_smtp_mail_from);
188 }
189 
190 static int
ruleset_match_smtp_rcpt_to(struct rule * r,const struct envelope * evp)191 ruleset_match_smtp_rcpt_to(struct rule *r, const struct envelope *evp)
192 {
193 	int		ret;
194 	const char	*key;
195 	struct table	*table;
196 	enum table_service service = K_MAILADDR;
197 
198 	if (!r->flag_smtp_rcpt_to)
199 		return 1;
200 
201 	if (r->flag_smtp_rcpt_to_regex)
202 		service = K_REGEX;
203 
204 	if ((key = mailaddr_to_text(&evp->dest)) == NULL)
205 		return -1;
206 
207 	table = table_find(env, r->table_smtp_rcpt_to);
208 	ret = table_match(table, service, key);
209 
210 	return MATCH_RESULT(ret, r->flag_smtp_rcpt_to);
211 }
212 
213 struct rule *
ruleset_match(const struct envelope * evp)214 ruleset_match(const struct envelope *evp)
215 {
216 	struct rule	*r;
217 	int		i = 0;
218 
219 #define	MATCH_EVAL(x)				\
220 	switch ((x)) {				\
221 	case -1:	goto tempfail;		\
222 	case 0:		continue;		\
223 	default:	break;			\
224 	}
225 	TAILQ_FOREACH(r, env->sc_rules, r_entry) {
226 		++i;
227 		MATCH_EVAL(ruleset_match_tag(r, evp));
228 		MATCH_EVAL(ruleset_match_from(r, evp));
229 		MATCH_EVAL(ruleset_match_to(r, evp));
230 		MATCH_EVAL(ruleset_match_smtp_helo(r, evp));
231 		MATCH_EVAL(ruleset_match_smtp_auth(r, evp));
232 		MATCH_EVAL(ruleset_match_smtp_starttls(r, evp));
233 		MATCH_EVAL(ruleset_match_smtp_mail_from(r, evp));
234 		MATCH_EVAL(ruleset_match_smtp_rcpt_to(r, evp));
235 		goto matched;
236 	}
237 #undef	MATCH_EVAL
238 
239 	errno = 0;
240 	log_trace(TRACE_RULES, "no rule matched");
241 	return (NULL);
242 
243 tempfail:
244 	errno = EAGAIN;
245 	log_trace(TRACE_RULES, "temporary failure in processing of a rule");
246 	return (NULL);
247 
248 matched:
249 	log_trace(TRACE_RULES, "rule #%d matched: %s", i, rule_to_text(r));
250 	return r;
251 }
252