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 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 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 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 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 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 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 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 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 * 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