xref: /openbsd-src/usr.sbin/smtpd/ruleset.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: ruleset.c,v 1.21 2012/05/13 00:10:49 gilles Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Gilles Chehade <gilles@openbsd.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/param.h>
23 #include <sys/socket.h>
24 
25 #include <netinet/in.h>
26 
27 #include <event.h>
28 #include <imsg.h>
29 #include <stdio.h>
30 #include <string.h>
31 
32 #include "smtpd.h"
33 #include "log.h"
34 
35 
36 struct rule *ruleset_match(struct envelope *);
37 
38 static int ruleset_check_source(struct map *, struct sockaddr_storage *);
39 static int ruleset_match_mask(struct sockaddr_storage *, struct netaddr *);
40 static int ruleset_inet4_match(struct sockaddr_in *, struct netaddr *);
41 static int ruleset_inet6_match(struct sockaddr_in6 *, struct netaddr *);
42 
43 
44 struct rule *
45 ruleset_match(struct envelope *evp)
46 {
47 	struct rule *r;
48 	struct map *map;
49 	struct mapel *me;
50 	struct mailaddr *maddr = &evp->dest;
51 	struct sockaddr_storage *ss = &evp->ss;
52 
53 	if (evp->flags & DF_INTERNAL)
54 		ss = NULL;
55 
56 	TAILQ_FOREACH(r, env->sc_rules, r_entry) {
57 
58 		if (r->r_tag[0] != '\0' && strcmp(r->r_tag, evp->tag) != 0)
59 			continue;
60 
61 		if (ss != NULL &&
62 		    (!(evp->flags & DF_AUTHENTICATED) &&
63 			! ruleset_check_source(r->r_sources, ss)))
64 			continue;
65 
66 		if (r->r_condition.c_type == C_ALL)
67 			return r;
68 
69 		if (r->r_condition.c_type == C_DOM) {
70 			map = map_find(r->r_condition.c_map);
71 			if (map == NULL)
72 				fatal("failed to lookup map.");
73 
74 			if (map->m_src == S_NONE) {
75 				TAILQ_FOREACH(me, &map->m_contents, me_entry) {
76 					if (hostname_match(maddr->domain,
77 						me->me_key.med_string))
78 						return r;
79 				}
80 			}
81 			else if (map_lookup(map->m_id, maddr->domain,
82 				K_VIRTUAL) != NULL) {
83 				return r;
84 			}
85 		}
86 
87 		if (r->r_condition.c_type == C_VDOM) {
88 			if (aliases_vdomain_exists(r->r_condition.c_map,
89 				maddr->domain))
90 				return r;
91 		}
92 	}
93 
94 	return NULL;
95 }
96 
97 static int
98 ruleset_cmp_source(char *s1, char *s2)
99 {
100 	struct netaddr n1;
101 	struct netaddr n2;
102 
103 	if (! text_to_netaddr(&n1, s1))
104 		return 0;
105 
106 	if (! text_to_netaddr(&n2, s2))
107 		return 0;
108 
109 	if (n1.ss.ss_family != n2.ss.ss_family)
110 		return 0;
111 	if (n1.ss.ss_len != n2.ss.ss_len)
112 		return 0;
113 
114 	return ruleset_match_mask(&n1.ss, &n2);
115 }
116 
117 static int
118 ruleset_check_source(struct map *map, struct sockaddr_storage *ss)
119 {
120 	struct mapel *me;
121 
122 	if (ss == NULL) {
123 		/* This happens when caller is part of an internal
124 		 * lookup (ie: alias resolved to a remote address)
125 		 */
126 		return 1;
127 	}
128 
129 	if (map->m_src == S_NONE) {
130 		TAILQ_FOREACH(me, &map->m_contents, me_entry) {
131 			if (ruleset_cmp_source(ss_to_text(ss),
132 				me->me_key.med_string))
133 				return 1;
134 		}
135 	}
136 	else {
137 		if (map_compare(map->m_id, ss_to_text(ss), K_NETADDR,
138 			    ruleset_cmp_source))
139 			return 1;
140 	}
141 
142 	return 0;
143 }
144 
145 static int
146 ruleset_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask)
147 {
148 	if (ss->ss_family == AF_INET)
149 		return ruleset_inet4_match((struct sockaddr_in *)ss, ssmask);
150 
151 	if (ss->ss_family == AF_INET6)
152 		return ruleset_inet6_match((struct sockaddr_in6 *)ss, ssmask);
153 
154 	return (0);
155 }
156 
157 static int
158 ruleset_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask)
159 {
160 	in_addr_t mask;
161 	int i;
162 
163 	/* a.b.c.d/8 -> htonl(0xff000000) */
164 	mask = 0;
165 	for (i = 0; i < ssmask->bits; ++i)
166 		mask = (mask >> 1) | 0x80000000;
167 	mask = htonl(mask);
168 
169 	/* (addr & mask) == (net & mask) */
170  	if ((ss->sin_addr.s_addr & mask) ==
171 	    (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask))
172 		return 1;
173 
174 	return 0;
175 }
176 
177 static int
178 ruleset_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask)
179 {
180 	struct in6_addr	*in;
181 	struct in6_addr	*inmask;
182 	struct in6_addr	 mask;
183 	int		 i;
184 
185 	bzero(&mask, sizeof(mask));
186 	for (i = 0; i < ssmask->bits / 8; i++)
187 		mask.s6_addr[i] = 0xff;
188 	i = ssmask->bits % 8;
189 	if (i)
190 		mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i;
191 
192 	in = &ss->sin6_addr;
193 	inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr;
194 
195 	for (i = 0; i < 16; i++) {
196 		if ((in->s6_addr[i] & mask.s6_addr[i]) !=
197 		    (inmask->s6_addr[i] & mask.s6_addr[i]))
198 			return (0);
199 	}
200 
201 	return (1);
202 }
203