xref: /openbsd-src/usr.sbin/smtpd/ruleset.c (revision 5054e3e78af0749a9bb00ba9a024b3ee2d90290f)
1 /*	$OpenBSD: ruleset.c,v 1.8 2009/11/03 22:57:41 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 #include <arpa/inet.h>
27 
28 #include <db.h>
29 #include <errno.h>
30 #include <event.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 
36 #include "smtpd.h"
37 
38 struct rule    *ruleset_match(struct smtpd *, char *tag, struct path *, struct sockaddr_storage *);
39 int		ruleset_check_source(struct map *, struct sockaddr_storage *);
40 int		ruleset_match_mask(struct sockaddr_storage *, struct netaddr *);
41 
42 
43 struct rule *
44 ruleset_match(struct smtpd *env, char *tag, struct path *path, struct sockaddr_storage *ss)
45 {
46 	struct rule *r;
47 	struct cond *cond;
48 	struct map *map;
49 	struct mapel *me;
50 
51 	TAILQ_FOREACH(r, env->sc_rules, r_entry) {
52 
53 		if (r->r_tag[0] != '\0' && strcmp(r->r_tag, tag) != 0)
54 			continue;
55 
56 		if (ss != NULL &&
57 		    (!(path->flags & F_PATH_AUTHENTICATED) &&
58 			! ruleset_check_source(r->r_sources, ss)))
59 			continue;
60 
61 		TAILQ_FOREACH(cond, &r->r_conditions, c_entry) {
62 			if (cond->c_type == C_ALL) {
63 				path->cond = cond;
64 				return r;
65 			}
66 
67 			if (cond->c_type == C_DOM) {
68 				map = map_find(env, cond->c_map);
69 				if (map == NULL)
70 					fatal("failed to lookup map.");
71 
72 				switch (map->m_src) {
73 				case S_NONE:
74 					TAILQ_FOREACH(me, &map->m_contents, me_entry) {
75 						if (hostname_match(path->domain, me->me_key.med_string)) {
76 							path->cond = cond;
77 							return r;
78 						}
79 					}
80 					break;
81 				case S_DB:
82 					if (map_dblookup(env, map->m_id, path->domain) != NULL) {
83 						path->cond = cond;
84 						return r;
85 					}
86 					break;
87 				default:
88 					log_info("unsupported map source for domain map");
89 					continue;
90 				}
91 			}
92 
93 			if (cond->c_type == C_VDOM) {
94 				if (aliases_vdomain_exists(env, cond->c_map, path->domain)) {
95 					path->cond = cond;
96 					return r;
97 				}
98 			}
99 		}
100 	}
101 
102 	return NULL;
103 }
104 
105 int
106 ruleset_check_source(struct map *map, struct sockaddr_storage *ss)
107 {
108 	struct mapel *me;
109 
110 	if (ss == NULL) {
111 		/* This happens when caller is part of an internal
112 		 * lookup (ie: alias resolved to a remote address)
113 		 */
114 		return 1;
115 	}
116 
117 	TAILQ_FOREACH(me, &map->m_contents, me_entry) {
118 
119 		if (ss->ss_family != me->me_key.med_addr.ss.ss_family)
120 			continue;
121 
122 		if (ss->ss_len != me->me_key.med_addr.ss.ss_len)
123 			continue;
124 
125 		if (ruleset_match_mask(ss, &me->me_key.med_addr))
126 			return 1;
127 	}
128 
129 	return 0;
130 }
131 
132 int
133 ruleset_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask)
134 {
135 	if (ss->ss_family == AF_INET) {
136 		struct sockaddr_in *ssin = (struct sockaddr_in *)ss;
137 		struct sockaddr_in *ssinmask = (struct sockaddr_in *)&ssmask->ss;
138 
139 		if ((ssin->sin_addr.s_addr & ssinmask->sin_addr.s_addr) ==
140 		    ssinmask->sin_addr.s_addr)
141 			return (1);
142 		return (0);
143 	}
144 
145 	if (ss->ss_family == AF_INET6) {
146 		struct in6_addr	*in;
147 		struct in6_addr	*inmask;
148 		struct in6_addr	 mask;
149 		int		 i;
150 
151 		bzero(&mask, sizeof(mask));
152 		for (i = 0; i < (128 - ssmask->bits) / 8; i++)
153 			mask.s6_addr[i] = 0xff;
154 		i = ssmask->bits % 8;
155 		if (i)
156 			mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i;
157 
158 		in = &((struct sockaddr_in6 *)ss)->sin6_addr;
159 		inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr;
160 
161 		for (i = 0; i < 16; i++) {
162 			if ((in->s6_addr[i] & mask.s6_addr[i]) !=
163 			    inmask->s6_addr[i])
164 				return (0);
165 		}
166 		return (1);
167 	}
168 
169 	return (0);
170 }
171