xref: /openbsd-src/usr.sbin/smtpd/ruleset.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: ruleset.c,v 1.19 2011/05/21 18:39:03 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->delivery.rcpt;
51 	struct sockaddr_storage *ss = &evp->delivery.ss;
52 
53 	if (evp->delivery.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->delivery.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 			switch (map->m_src) {
75 			case S_NONE:
76 				TAILQ_FOREACH(me, &map->m_contents, me_entry) {
77 					if (hostname_match(maddr->domain, me->me_key.med_string))
78 						return r;
79 				}
80 				break;
81 			case S_DB:
82 				if (map_lookup(map->m_id, maddr->domain, K_VIRTUAL) != NULL)
83 					return r;
84 				break;
85 			default:
86 				log_info("unsupported map source for domain map");
87 				continue;
88 			}
89 		}
90 
91 		if (r->r_condition.c_type == C_VDOM)
92 			if (aliases_vdomain_exists(r->r_condition.c_map, maddr->domain))
93 				return r;
94 	}
95 
96 	return NULL;
97 }
98 
99 static int
100 ruleset_check_source(struct map *map, struct sockaddr_storage *ss)
101 {
102 	struct mapel *me;
103 
104 	if (ss == NULL) {
105 		/* This happens when caller is part of an internal
106 		 * lookup (ie: alias resolved to a remote address)
107 		 */
108 		return 1;
109 	}
110 
111 	TAILQ_FOREACH(me, &map->m_contents, me_entry) {
112 
113 		if (ss->ss_family != me->me_key.med_addr.ss.ss_family)
114 			continue;
115 
116 		if (ss->ss_len != me->me_key.med_addr.ss.ss_len)
117 			continue;
118 
119 		if (ruleset_match_mask(ss, &me->me_key.med_addr))
120 			return 1;
121 	}
122 
123 	return 0;
124 }
125 
126 static int
127 ruleset_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask)
128 {
129 	if (ss->ss_family == AF_INET)
130 		return ruleset_inet4_match((struct sockaddr_in *)ss, ssmask);
131 
132 	if (ss->ss_family == AF_INET6)
133 		return ruleset_inet6_match((struct sockaddr_in6 *)ss, ssmask);
134 
135 	return (0);
136 }
137 
138 static int
139 ruleset_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask)
140 {
141 	in_addr_t mask;
142 	int i;
143 
144 	/* a.b.c.d/8 -> htonl(0xff000000) */
145 	mask = 0;
146 	for (i = 0; i < ssmask->bits; ++i)
147 		mask = (mask >> 1) | 0x80000000;
148 	mask = htonl(mask);
149 
150 	/* (addr & mask) == (net & mask) */
151  	if ((ss->sin_addr.s_addr & mask) ==
152 	    (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask))
153 		return 1;
154 
155 	return 0;
156 }
157 
158 static int
159 ruleset_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask)
160 {
161 	struct in6_addr	*in;
162 	struct in6_addr	*inmask;
163 	struct in6_addr	 mask;
164 	int		 i;
165 
166 	bzero(&mask, sizeof(mask));
167 	for (i = 0; i < ssmask->bits / 8; i++)
168 		mask.s6_addr[i] = 0xff;
169 	i = ssmask->bits % 8;
170 	if (i)
171 		mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i;
172 
173 	in = &ss->sin6_addr;
174 	inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr;
175 
176 	for (i = 0; i < 16; i++) {
177 		if ((in->s6_addr[i] & mask.s6_addr[i]) !=
178 		    (inmask->s6_addr[i] & mask.s6_addr[i]))
179 			return (0);
180 	}
181 
182 	return (1);
183 }
184