xref: /openbsd-src/usr.sbin/smtpd/ruleset.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: ruleset.c,v 1.33 2016/08/31 15:24:04 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 
37 static int ruleset_check_source(struct table *,
38     const struct sockaddr_storage *, int);
39 static int ruleset_check_mailaddr(struct table *, const struct mailaddr *);
40 
41 struct rule *
42 ruleset_match(const struct envelope *evp)
43 {
44 	const struct mailaddr		*maddr = &evp->dest;
45 	const struct sockaddr_storage	*ss = &evp->ss;
46 	struct rule			*r;
47 	int				 ret;
48 
49 	TAILQ_FOREACH(r, env->sc_rules, r_entry) {
50 
51 		if (r->r_tag[0] != '\0') {
52 			ret = strcmp(r->r_tag, evp->tag);
53 			if (ret != 0 && !r->r_nottag)
54 				continue;
55 			if (ret == 0 && r->r_nottag)
56 				continue;
57 		}
58 
59 		if (r->r_wantauth && !(evp->flags & EF_AUTHENTICATED))
60 			continue;
61 
62 		ret = ruleset_check_source(r->r_sources, ss, evp->flags);
63 		if (ret == -1) {
64 			errno = EAGAIN;
65 			return (NULL);
66 		}
67 		if ((ret == 0 && !r->r_notsources) || (ret != 0 && r->r_notsources))
68 			continue;
69 
70 		if (r->r_senders) {
71 			ret = ruleset_check_mailaddr(r->r_senders, &evp->sender);
72 			if (ret == -1) {
73 				errno = EAGAIN;
74 				return (NULL);
75 			}
76 			if ((ret == 0 && !r->r_notsenders) || (ret != 0 && r->r_notsenders))
77 				continue;
78 		}
79 
80 		if (r->r_recipients) {
81 			ret = ruleset_check_mailaddr(r->r_recipients, &evp->dest);
82 			if (ret == -1) {
83 				errno = EAGAIN;
84 				return (NULL);
85 			}
86 			if ((ret == 0 && !r->r_notrecipients) || (ret != 0 && r->r_notrecipients))
87 				continue;
88 		}
89 
90 		ret = r->r_destination == NULL ? 1 :
91 		    table_lookup(r->r_destination, NULL, maddr->domain, K_DOMAIN,
92 			NULL);
93 		if (ret == -1) {
94 			errno = EAGAIN;
95 			return NULL;
96 		}
97 		if ((ret == 0 && !r->r_notdestination) || (ret != 0 && r->r_notdestination))
98 			continue;
99 
100 		goto matched;
101 	}
102 
103 	errno = 0;
104 	log_trace(TRACE_RULES, "no rule matched");
105 	return (NULL);
106 
107 matched:
108 	log_trace(TRACE_RULES, "rule matched: %s", rule_to_text(r));
109 	return r;
110 }
111 
112 static int
113 ruleset_check_source(struct table *table, const struct sockaddr_storage *ss,
114     int evpflags)
115 {
116 	const char   *key;
117 
118 	if (evpflags & (EF_AUTHENTICATED | EF_INTERNAL))
119 		key = "local";
120 	else
121 		key = ss_to_text(ss);
122 	switch (table_lookup(table, NULL, key, K_NETADDR, NULL)) {
123 	case 1:
124 		return 1;
125 	case -1:
126 		log_warnx("warn: failure to perform a table lookup on table %s",
127 		    table->t_name);
128 		return -1;
129 	default:
130 		break;
131 	}
132 
133 	return 0;
134 }
135 
136 static int
137 ruleset_check_mailaddr(struct table *table, const struct mailaddr *maddr)
138 {
139 	const char	*key;
140 
141 	key = mailaddr_to_text(maddr);
142 	if (key == NULL)
143 		return -1;
144 
145 	switch (table_lookup(table, NULL, key, K_MAILADDR, NULL)) {
146 	case 1:
147 		return 1;
148 	case -1:
149 		log_warnx("warn: failure to perform a table lookup on table %s",
150 		    table->t_name);
151 		return -1;
152 	default:
153 		break;
154 	}
155 	return 0;
156 }
157