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