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