xref: /openbsd/usr.sbin/smtpd/ruleset.c (revision 699c3f98)
1 /*	$OpenBSD: ruleset.c,v 1.39 2018/12/26 14:15:12 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 <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/tree.h>
22 #include <sys/socket.h>
23 
24 #include <netinet/in.h>
25 
26 #include <errno.h>
27 #include <event.h>
28 #include <imsg.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <limits.h>
32 
33 #include "smtpd.h"
34 #include "log.h"
35 
36 
37 static int
38 ruleset_match_table_lookup(struct table *table, const char *key, enum table_service service)
39 {
40 	switch (table_lookup(table, key, service, NULL)) {
41 	case 1:
42 		return 1;
43 	case -1:
44 		log_warnx("warn: failure to perform a table lookup on table %s",
45 		    table->t_name);
46 		return -1;
47 	default:
48 		break;
49 	}
50 	return 0;
51 }
52 
53 static int
54 ruleset_match_tag(struct rule *r, const struct envelope *evp)
55 {
56 	int		ret;
57 	struct table	*table;
58 	enum table_service service = K_STRING;
59 
60 	if (!r->flag_tag)
61 		return 1;
62 
63 	if (r->flag_tag_regex)
64 		service = K_REGEX;
65 
66 	table = table_find(env, r->table_tag, NULL);
67 	if ((ret = ruleset_match_table_lookup(table, evp->tag, service)) < 0)
68 		return ret;
69 
70 	return r->flag_tag < 0 ? !ret : ret;
71 }
72 
73 static int
74 ruleset_match_from(struct rule *r, const struct envelope *evp)
75 {
76 	int		ret;
77 	const char	*key;
78 	struct table	*table;
79 	enum table_service service = K_NETADDR;
80 
81 	if (!r->flag_from)
82 		return 1;
83 
84 	if (r->flag_from_socket) {
85 		/* XXX - socket needs to be distinguished from "local" */
86 		return -1;
87 	}
88 
89 	if (evp->flags & EF_INTERNAL)
90 		key = "local";
91 	else if (r->flag_from_rdns) {
92 		if (strcmp(evp->hostname, "<unknown>") == 0)
93 			return 0;
94 		key = evp->hostname;
95 	}
96 	else
97 		key = ss_to_text(&evp->ss);
98 
99 	if (r->flag_from_regex)
100 		service = K_REGEX;
101 
102 	table = table_find(env, r->table_from, NULL);
103 	if ((ret = ruleset_match_table_lookup(table, key, service)) < 0)
104 		return -1;
105 
106 	return r->flag_from < 0 ? !ret : ret;
107 }
108 
109 static int
110 ruleset_match_to(struct rule *r, const struct envelope *evp)
111 {
112 	int		ret;
113 	struct table	*table;
114 	enum table_service service = K_DOMAIN;
115 
116 	if (!r->flag_for)
117 		return 1;
118 
119 	if (r->flag_for_regex)
120 		service = K_REGEX;
121 
122 	table = table_find(env, r->table_for, NULL);
123 	if ((ret = ruleset_match_table_lookup(table, evp->dest.domain,
124 		    service)) < 0)
125 		return -1;
126 
127 	return r->flag_for < 0 ? !ret : ret;
128 }
129 
130 static int
131 ruleset_match_smtp_helo(struct rule *r, const struct envelope *evp)
132 {
133 	int		ret;
134 	struct table	*table;
135 	enum table_service service = K_DOMAIN;
136 
137 	if (!r->flag_smtp_helo)
138 		return 1;
139 
140 	if (r->flag_smtp_helo_regex)
141 		service = K_REGEX;
142 
143 	table = table_find(env, r->table_smtp_helo, NULL);
144 	if ((ret = ruleset_match_table_lookup(table, evp->helo, service)) < 0)
145 		return -1;
146 
147 	return r->flag_smtp_helo < 0 ? !ret : ret;
148 }
149 
150 static int
151 ruleset_match_smtp_starttls(struct rule *r, const struct envelope *evp)
152 {
153 	if (!r->flag_smtp_starttls)
154 		return 1;
155 
156 	/* XXX - not until TLS flag is added to envelope */
157 	return -1;
158 }
159 
160 static int
161 ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp)
162 {
163 	int	ret;
164 
165 	if (!r->flag_smtp_auth)
166 		return 1;
167 
168 	if (!(evp->flags & EF_AUTHENTICATED))
169 		ret = 0;
170 	else if (r->table_smtp_auth) {
171 		/* XXX - not until smtp_session->username is added to envelope */
172 		/*
173 		 * table = table_find(m->from_table, NULL);
174 		 * key = evp->username;
175 		 * return ruleset_match_table_lookup(table, key, K_CREDENTIALS);
176 		 */
177 		return -1;
178 
179 	}
180 	else
181 		ret = 1;
182 
183 	return r->flag_smtp_auth < 0 ? !ret : ret;
184 }
185 
186 static int
187 ruleset_match_smtp_mail_from(struct rule *r, const struct envelope *evp)
188 {
189 	int		ret;
190 	const char	*key;
191 	struct table	*table;
192 	enum table_service service = K_MAILADDR;
193 
194 	if (!r->flag_smtp_mail_from)
195 		return 1;
196 
197 	if (r->flag_smtp_mail_from_regex)
198 		service = K_REGEX;
199 
200 	if ((key = mailaddr_to_text(&evp->sender)) == NULL)
201 		return -1;
202 
203 	table = table_find(env, r->table_smtp_mail_from, NULL);
204 	if ((ret = ruleset_match_table_lookup(table, key, service)) < 0)
205 		return -1;
206 
207 	return r->flag_smtp_mail_from < 0 ? !ret : ret;
208 }
209 
210 static int
211 ruleset_match_smtp_rcpt_to(struct rule *r, const struct envelope *evp)
212 {
213 	int		ret;
214 	const char	*key;
215 	struct table	*table;
216 	enum table_service service = K_MAILADDR;
217 
218 	if (!r->flag_smtp_rcpt_to)
219 		return 1;
220 
221 	if (r->flag_smtp_rcpt_to_regex)
222 		service = K_REGEX;
223 
224 	if ((key = mailaddr_to_text(&evp->dest)) == NULL)
225 		return -1;
226 
227 	table = table_find(env, r->table_smtp_rcpt_to, NULL);
228 	if ((ret = ruleset_match_table_lookup(table, key, service)) < 0)
229 		return -1;
230 
231 	return r->flag_smtp_rcpt_to < 0 ? !ret : ret;
232 }
233 
234 struct rule *
235 ruleset_match(const struct envelope *evp)
236 {
237 	struct rule	*r;
238 	int		i = 0;
239 
240 #define	MATCH_EVAL(x)				\
241 	switch ((x)) {				\
242 	case -1:	goto tempfail;		\
243 	case 0:		continue;		\
244 	default:	break;			\
245 	}
246 	TAILQ_FOREACH(r, env->sc_rules, r_entry) {
247 		++i;
248 		MATCH_EVAL(ruleset_match_tag(r, evp));
249 		MATCH_EVAL(ruleset_match_from(r, evp));
250 		MATCH_EVAL(ruleset_match_to(r, evp));
251 		MATCH_EVAL(ruleset_match_smtp_helo(r, evp));
252 		MATCH_EVAL(ruleset_match_smtp_auth(r, evp));
253 		MATCH_EVAL(ruleset_match_smtp_starttls(r, evp));
254 		MATCH_EVAL(ruleset_match_smtp_mail_from(r, evp));
255 		MATCH_EVAL(ruleset_match_smtp_rcpt_to(r, evp));
256 		goto matched;
257 	}
258 #undef	MATCH_EVAL
259 
260 	errno = 0;
261 	log_trace(TRACE_RULES, "no rule matched");
262 	return (NULL);
263 
264 tempfail:
265 	errno = EAGAIN;
266 	log_trace(TRACE_RULES, "temporary failure in processing of a rule");
267 	return (NULL);
268 
269 matched:
270 	log_trace(TRACE_RULES, "rule #%d matched: %s", i, rule_to_text(r));
271 	return r;
272 }
273