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