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