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