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