1*c1f5f430Sdjm /* $OpenBSD: addrmatch.c,v 1.3 2008/06/10 23:06:19 djm Exp $ */ 24e6f7e3dSdjm 34e6f7e3dSdjm /* 44e6f7e3dSdjm * Copyright (c) 2004-2008 Damien Miller <djm@mindrot.org> 54e6f7e3dSdjm * 64e6f7e3dSdjm * Permission to use, copy, modify, and distribute this software for any 74e6f7e3dSdjm * purpose with or without fee is hereby granted, provided that the above 84e6f7e3dSdjm * copyright notice and this permission notice appear in all copies. 94e6f7e3dSdjm * 104e6f7e3dSdjm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 114e6f7e3dSdjm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 124e6f7e3dSdjm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 134e6f7e3dSdjm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 144e6f7e3dSdjm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 154e6f7e3dSdjm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 164e6f7e3dSdjm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 174e6f7e3dSdjm */ 184e6f7e3dSdjm 194e6f7e3dSdjm #include <sys/types.h> 204e6f7e3dSdjm #include <sys/socket.h> 214e6f7e3dSdjm #include <netinet/in.h> 224e6f7e3dSdjm #include <arpa/inet.h> 234e6f7e3dSdjm 244e6f7e3dSdjm #include <netdb.h> 254e6f7e3dSdjm #include <string.h> 264e6f7e3dSdjm #include <stdlib.h> 274e6f7e3dSdjm #include <stdio.h> 284e6f7e3dSdjm #include <stdarg.h> 294e6f7e3dSdjm 304e6f7e3dSdjm #include "match.h" 314e6f7e3dSdjm #include "log.h" 324e6f7e3dSdjm 334e6f7e3dSdjm struct xaddr { 344e6f7e3dSdjm sa_family_t af; 354e6f7e3dSdjm union { 364e6f7e3dSdjm struct in_addr v4; 374e6f7e3dSdjm struct in6_addr v6; 384e6f7e3dSdjm u_int8_t addr8[16]; 394e6f7e3dSdjm u_int32_t addr32[4]; 404e6f7e3dSdjm } xa; /* 128-bit address */ 414e6f7e3dSdjm u_int32_t scope_id; /* iface scope id for v6 */ 424e6f7e3dSdjm #define v4 xa.v4 434e6f7e3dSdjm #define v6 xa.v6 444e6f7e3dSdjm #define addr8 xa.addr8 454e6f7e3dSdjm #define addr32 xa.addr32 464e6f7e3dSdjm }; 474e6f7e3dSdjm 484e6f7e3dSdjm static int 494e6f7e3dSdjm addr_unicast_masklen(int af) 504e6f7e3dSdjm { 514e6f7e3dSdjm switch (af) { 524e6f7e3dSdjm case AF_INET: 534e6f7e3dSdjm return 32; 544e6f7e3dSdjm case AF_INET6: 554e6f7e3dSdjm return 128; 564e6f7e3dSdjm default: 574e6f7e3dSdjm return -1; 584e6f7e3dSdjm } 594e6f7e3dSdjm } 604e6f7e3dSdjm 614e6f7e3dSdjm static inline int 624e6f7e3dSdjm masklen_valid(int af, u_int masklen) 634e6f7e3dSdjm { 644e6f7e3dSdjm switch (af) { 654e6f7e3dSdjm case AF_INET: 664e6f7e3dSdjm return masklen <= 32 ? 0 : -1; 674e6f7e3dSdjm case AF_INET6: 684e6f7e3dSdjm return masklen <= 128 ? 0 : -1; 694e6f7e3dSdjm default: 704e6f7e3dSdjm return -1; 714e6f7e3dSdjm } 724e6f7e3dSdjm } 734e6f7e3dSdjm 744e6f7e3dSdjm /* 754e6f7e3dSdjm * Convert struct sockaddr to struct xaddr 764e6f7e3dSdjm * Returns 0 on success, -1 on failure. 774e6f7e3dSdjm */ 784e6f7e3dSdjm static int 794e6f7e3dSdjm addr_sa_to_xaddr(struct sockaddr *sa, socklen_t slen, struct xaddr *xa) 804e6f7e3dSdjm { 814e6f7e3dSdjm struct sockaddr_in *in4 = (struct sockaddr_in *)sa; 824e6f7e3dSdjm struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa; 834e6f7e3dSdjm 844e6f7e3dSdjm memset(xa, '\0', sizeof(*xa)); 854e6f7e3dSdjm 864e6f7e3dSdjm switch (sa->sa_family) { 874e6f7e3dSdjm case AF_INET: 884e6f7e3dSdjm if (slen < sizeof(*in4)) 894e6f7e3dSdjm return -1; 904e6f7e3dSdjm xa->af = AF_INET; 914e6f7e3dSdjm memcpy(&xa->v4, &in4->sin_addr, sizeof(xa->v4)); 924e6f7e3dSdjm break; 934e6f7e3dSdjm case AF_INET6: 944e6f7e3dSdjm if (slen < sizeof(*in6)) 954e6f7e3dSdjm return -1; 964e6f7e3dSdjm xa->af = AF_INET6; 974e6f7e3dSdjm memcpy(&xa->v6, &in6->sin6_addr, sizeof(xa->v6)); 984e6f7e3dSdjm xa->scope_id = in6->sin6_scope_id; 994e6f7e3dSdjm break; 1004e6f7e3dSdjm default: 1014e6f7e3dSdjm return -1; 1024e6f7e3dSdjm } 1034e6f7e3dSdjm 1044e6f7e3dSdjm return 0; 1054e6f7e3dSdjm } 1064e6f7e3dSdjm 1074e6f7e3dSdjm /* 1084e6f7e3dSdjm * Calculate a netmask of length 'l' for address family 'af' and 1094e6f7e3dSdjm * store it in 'n'. 1104e6f7e3dSdjm * Returns 0 on success, -1 on failure. 1114e6f7e3dSdjm */ 1124e6f7e3dSdjm static int 1134e6f7e3dSdjm addr_netmask(int af, u_int l, struct xaddr *n) 1144e6f7e3dSdjm { 1154e6f7e3dSdjm int i; 1164e6f7e3dSdjm 1174e6f7e3dSdjm if (masklen_valid(af, l) != 0 || n == NULL) 1184e6f7e3dSdjm return -1; 1194e6f7e3dSdjm 1204e6f7e3dSdjm memset(n, '\0', sizeof(*n)); 1214e6f7e3dSdjm switch (af) { 1224e6f7e3dSdjm case AF_INET: 1234e6f7e3dSdjm n->af = AF_INET; 1244e6f7e3dSdjm n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff); 1254e6f7e3dSdjm return 0; 1264e6f7e3dSdjm case AF_INET6: 1274e6f7e3dSdjm n->af = AF_INET6; 1284e6f7e3dSdjm for (i = 0; i < 4 && l >= 32; i++, l -= 32) 1294e6f7e3dSdjm n->addr32[i] = 0xffffffffU; 1304e6f7e3dSdjm if (i < 4 && l != 0) 1314e6f7e3dSdjm n->addr32[i] = htonl((0xffffffff << (32 - l)) & 1324e6f7e3dSdjm 0xffffffff); 1334e6f7e3dSdjm return 0; 1344e6f7e3dSdjm default: 1354e6f7e3dSdjm return -1; 1364e6f7e3dSdjm } 1374e6f7e3dSdjm } 1384e6f7e3dSdjm 1394e6f7e3dSdjm /* 1404e6f7e3dSdjm * Perform logical AND of addresses 'a' and 'b', storing result in 'dst'. 1414e6f7e3dSdjm * Returns 0 on success, -1 on failure. 1424e6f7e3dSdjm */ 1434e6f7e3dSdjm static int 1444e6f7e3dSdjm addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b) 1454e6f7e3dSdjm { 1464e6f7e3dSdjm int i; 1474e6f7e3dSdjm 1484e6f7e3dSdjm if (dst == NULL || a == NULL || b == NULL || a->af != b->af) 1494e6f7e3dSdjm return -1; 1504e6f7e3dSdjm 1514e6f7e3dSdjm memcpy(dst, a, sizeof(*dst)); 1524e6f7e3dSdjm switch (a->af) { 1534e6f7e3dSdjm case AF_INET: 1544e6f7e3dSdjm dst->v4.s_addr &= b->v4.s_addr; 1554e6f7e3dSdjm return 0; 1564e6f7e3dSdjm case AF_INET6: 1574e6f7e3dSdjm dst->scope_id = a->scope_id; 1584e6f7e3dSdjm for (i = 0; i < 4; i++) 1594e6f7e3dSdjm dst->addr32[i] &= b->addr32[i]; 1604e6f7e3dSdjm return 0; 1614e6f7e3dSdjm default: 1624e6f7e3dSdjm return -1; 1634e6f7e3dSdjm } 1644e6f7e3dSdjm } 1654e6f7e3dSdjm 1664e6f7e3dSdjm /* 1674e6f7e3dSdjm * Compare addresses 'a' and 'b' 1684e6f7e3dSdjm * Return 0 if addresses are identical, -1 if (a < b) or 1 if (a > b) 1694e6f7e3dSdjm */ 1704e6f7e3dSdjm static int 1714e6f7e3dSdjm addr_cmp(const struct xaddr *a, const struct xaddr *b) 1724e6f7e3dSdjm { 1734e6f7e3dSdjm int i; 1744e6f7e3dSdjm 1754e6f7e3dSdjm if (a->af != b->af) 1764e6f7e3dSdjm return a->af == AF_INET6 ? 1 : -1; 1774e6f7e3dSdjm 1784e6f7e3dSdjm switch (a->af) { 1794e6f7e3dSdjm case AF_INET: 1804e6f7e3dSdjm if (a->v4.s_addr == b->v4.s_addr) 1814e6f7e3dSdjm return 0; 1824e6f7e3dSdjm return ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr) ? 1 : -1; 1834e6f7e3dSdjm case AF_INET6: 1844e6f7e3dSdjm for (i = 0; i < 16; i++) 1854e6f7e3dSdjm if (a->addr8[i] - b->addr8[i] != 0) 1864e6f7e3dSdjm return a->addr8[i] > b->addr8[i] ? 1 : -1; 1874e6f7e3dSdjm if (a->scope_id == b->scope_id) 1884e6f7e3dSdjm return 0; 1894e6f7e3dSdjm return a->scope_id > b->scope_id ? 1 : -1; 1904e6f7e3dSdjm default: 1914e6f7e3dSdjm return -1; 1924e6f7e3dSdjm } 1934e6f7e3dSdjm } 1944e6f7e3dSdjm 1954e6f7e3dSdjm /* 1964e6f7e3dSdjm * Parse string address 'p' into 'n' 1974e6f7e3dSdjm * Returns 0 on success, -1 on failure. 1984e6f7e3dSdjm */ 1994e6f7e3dSdjm static int 2004e6f7e3dSdjm addr_pton(const char *p, struct xaddr *n) 2014e6f7e3dSdjm { 2024e6f7e3dSdjm struct addrinfo hints, *ai; 2034e6f7e3dSdjm 2044e6f7e3dSdjm memset(&hints, '\0', sizeof(hints)); 2054e6f7e3dSdjm hints.ai_flags = AI_NUMERICHOST; 2064e6f7e3dSdjm 2074e6f7e3dSdjm if (p == NULL || getaddrinfo(p, NULL, &hints, &ai) != 0) 2084e6f7e3dSdjm return -1; 2094e6f7e3dSdjm 2104e6f7e3dSdjm if (ai == NULL || ai->ai_addr == NULL) 2114e6f7e3dSdjm return -1; 2124e6f7e3dSdjm 2134e6f7e3dSdjm if (n != NULL && 2144e6f7e3dSdjm addr_sa_to_xaddr(ai->ai_addr, ai->ai_addrlen, n) == -1) { 2154e6f7e3dSdjm freeaddrinfo(ai); 2164e6f7e3dSdjm return -1; 2174e6f7e3dSdjm } 2184e6f7e3dSdjm 2194e6f7e3dSdjm freeaddrinfo(ai); 2204e6f7e3dSdjm return 0; 2214e6f7e3dSdjm } 2224e6f7e3dSdjm 2234e6f7e3dSdjm /* 2244e6f7e3dSdjm * Perform bitwise negation of address 2254e6f7e3dSdjm * Returns 0 on success, -1 on failure. 2264e6f7e3dSdjm */ 2274e6f7e3dSdjm static int 2284e6f7e3dSdjm addr_invert(struct xaddr *n) 2294e6f7e3dSdjm { 2304e6f7e3dSdjm int i; 2314e6f7e3dSdjm 2324e6f7e3dSdjm if (n == NULL) 2334e6f7e3dSdjm return (-1); 2344e6f7e3dSdjm 2354e6f7e3dSdjm switch (n->af) { 2364e6f7e3dSdjm case AF_INET: 2374e6f7e3dSdjm n->v4.s_addr = ~n->v4.s_addr; 2384e6f7e3dSdjm return (0); 2394e6f7e3dSdjm case AF_INET6: 2404e6f7e3dSdjm for (i = 0; i < 4; i++) 2414e6f7e3dSdjm n->addr32[i] = ~n->addr32[i]; 2424e6f7e3dSdjm return (0); 2434e6f7e3dSdjm default: 2444e6f7e3dSdjm return (-1); 2454e6f7e3dSdjm } 2464e6f7e3dSdjm } 2474e6f7e3dSdjm 2484e6f7e3dSdjm /* 2494e6f7e3dSdjm * Calculate a netmask of length 'l' for address family 'af' and 2504e6f7e3dSdjm * store it in 'n'. 2514e6f7e3dSdjm * Returns 0 on success, -1 on failure. 2524e6f7e3dSdjm */ 2534e6f7e3dSdjm static int 2544e6f7e3dSdjm addr_hostmask(int af, u_int l, struct xaddr *n) 2554e6f7e3dSdjm { 2564e6f7e3dSdjm if (addr_netmask(af, l, n) == -1 || addr_invert(n) == -1) 2574e6f7e3dSdjm return (-1); 2584e6f7e3dSdjm return (0); 2594e6f7e3dSdjm } 2604e6f7e3dSdjm 2614e6f7e3dSdjm /* 2624e6f7e3dSdjm * Test whether address 'a' is all zeros (i.e. 0.0.0.0 or ::) 2634e6f7e3dSdjm * Returns 0 on if address is all-zeros, -1 if not all zeros or on failure. 2644e6f7e3dSdjm */ 2654e6f7e3dSdjm static int 2664e6f7e3dSdjm addr_is_all0s(const struct xaddr *a) 2674e6f7e3dSdjm { 2684e6f7e3dSdjm int i; 2694e6f7e3dSdjm 2704e6f7e3dSdjm switch (a->af) { 2714e6f7e3dSdjm case AF_INET: 2724e6f7e3dSdjm return (a->v4.s_addr == 0 ? 0 : -1); 2734e6f7e3dSdjm case AF_INET6:; 2744e6f7e3dSdjm for (i = 0; i < 4; i++) 2754e6f7e3dSdjm if (a->addr32[i] != 0) 2764e6f7e3dSdjm return (-1); 2774e6f7e3dSdjm return (0); 2784e6f7e3dSdjm default: 2794e6f7e3dSdjm return (-1); 2804e6f7e3dSdjm } 2814e6f7e3dSdjm } 2824e6f7e3dSdjm 2834e6f7e3dSdjm /* 2844e6f7e3dSdjm * Test whether host portion of address 'a', as determined by 'masklen' 2854e6f7e3dSdjm * is all zeros. 2864e6f7e3dSdjm * Returns 0 on if host portion of address is all-zeros, 2874e6f7e3dSdjm * -1 if not all zeros or on failure. 2884e6f7e3dSdjm */ 2894e6f7e3dSdjm static int 2904e6f7e3dSdjm addr_host_is_all0s(const struct xaddr *a, u_int masklen) 2914e6f7e3dSdjm { 2924e6f7e3dSdjm struct xaddr tmp_addr, tmp_mask, tmp_result; 2934e6f7e3dSdjm 2944e6f7e3dSdjm memcpy(&tmp_addr, a, sizeof(tmp_addr)); 2954e6f7e3dSdjm if (addr_hostmask(a->af, masklen, &tmp_mask) == -1) 2964e6f7e3dSdjm return (-1); 2974e6f7e3dSdjm if (addr_and(&tmp_result, &tmp_addr, &tmp_mask) == -1) 2984e6f7e3dSdjm return (-1); 2994e6f7e3dSdjm return (addr_is_all0s(&tmp_result)); 3004e6f7e3dSdjm } 3014e6f7e3dSdjm 3024e6f7e3dSdjm /* 3034e6f7e3dSdjm * Parse a CIDR address (x.x.x.x/y or xxxx:yyyy::/z). 3044e6f7e3dSdjm * Return -1 on parse error, -2 on inconsistency or 0 on success. 3054e6f7e3dSdjm */ 3064e6f7e3dSdjm static int 3074e6f7e3dSdjm addr_pton_cidr(const char *p, struct xaddr *n, u_int *l) 3084e6f7e3dSdjm { 3094e6f7e3dSdjm struct xaddr tmp; 3104e6f7e3dSdjm long unsigned int masklen = 999; 3114e6f7e3dSdjm char addrbuf[64], *mp, *cp; 3124e6f7e3dSdjm 3134e6f7e3dSdjm /* Don't modify argument */ 3144e6f7e3dSdjm if (p == NULL || strlcpy(addrbuf, p, sizeof(addrbuf)) > sizeof(addrbuf)) 3154e6f7e3dSdjm return -1; 3164e6f7e3dSdjm 3174e6f7e3dSdjm if ((mp = strchr(addrbuf, '/')) != NULL) { 3184e6f7e3dSdjm *mp = '\0'; 3194e6f7e3dSdjm mp++; 3204e6f7e3dSdjm masklen = strtoul(mp, &cp, 10); 3214e6f7e3dSdjm if (*mp == '\0' || *cp != '\0' || masklen > 128) 3224e6f7e3dSdjm return -1; 3234e6f7e3dSdjm } 3244e6f7e3dSdjm 3254e6f7e3dSdjm if (addr_pton(addrbuf, &tmp) == -1) 3264e6f7e3dSdjm return -1; 3274e6f7e3dSdjm 3284e6f7e3dSdjm if (mp == NULL) 3294e6f7e3dSdjm masklen = addr_unicast_masklen(tmp.af); 3304e6f7e3dSdjm if (masklen_valid(tmp.af, masklen) == -1) 3314e6f7e3dSdjm return -2; 3324e6f7e3dSdjm if (addr_host_is_all0s(&tmp, masklen) != 0) 3334e6f7e3dSdjm return -2; 3344e6f7e3dSdjm 3354e6f7e3dSdjm if (n != NULL) 3364e6f7e3dSdjm memcpy(n, &tmp, sizeof(*n)); 3374e6f7e3dSdjm if (l != NULL) 3384e6f7e3dSdjm *l = masklen; 3394e6f7e3dSdjm 3404e6f7e3dSdjm return 0; 3414e6f7e3dSdjm } 3424e6f7e3dSdjm 3434e6f7e3dSdjm static int 3444e6f7e3dSdjm addr_netmatch(const struct xaddr *host, const struct xaddr *net, u_int masklen) 3454e6f7e3dSdjm { 3464e6f7e3dSdjm struct xaddr tmp_mask, tmp_result; 3474e6f7e3dSdjm 3484e6f7e3dSdjm if (host->af != net->af) 3494e6f7e3dSdjm return -1; 3504e6f7e3dSdjm 3514e6f7e3dSdjm if (addr_netmask(host->af, masklen, &tmp_mask) == -1) 3524e6f7e3dSdjm return -1; 3534e6f7e3dSdjm if (addr_and(&tmp_result, host, &tmp_mask) == -1) 3544e6f7e3dSdjm return -1; 3554e6f7e3dSdjm return addr_cmp(&tmp_result, net); 3564e6f7e3dSdjm } 3574e6f7e3dSdjm 3584e6f7e3dSdjm /* 3594e6f7e3dSdjm * Match "addr" against list pattern list "_list", which may contain a 3604e6f7e3dSdjm * mix of CIDR addresses and old-school wildcards. 3614e6f7e3dSdjm * 3624e6f7e3dSdjm * If addr is NULL, then no matching is performed, but _list is parsed 3634e6f7e3dSdjm * and checked for well-formedness. 3644e6f7e3dSdjm * 3654e6f7e3dSdjm * Returns 1 on match found (never returned when addr == NULL). 3664e6f7e3dSdjm * Returns 0 on if no match found, or no errors found when addr == NULL. 367*c1f5f430Sdjm * Returns -1 on negated match found (never returned when addr == NULL). 368*c1f5f430Sdjm * Returns -2 on invalid list entry. 3694e6f7e3dSdjm */ 3704e6f7e3dSdjm int 3714e6f7e3dSdjm addr_match_list(const char *addr, const char *_list) 3724e6f7e3dSdjm { 3734e6f7e3dSdjm char *list, *cp, *o; 3744e6f7e3dSdjm struct xaddr try_addr, match_addr; 3754e6f7e3dSdjm u_int masklen, neg; 3764e6f7e3dSdjm int ret = 0, r; 3774e6f7e3dSdjm 378952f5cdcSdjm if (addr != NULL && addr_pton(addr, &try_addr) != 0) { 379952f5cdcSdjm debug2("%s: couldn't parse address %.100s", __func__, addr); 380952f5cdcSdjm return 0; 381952f5cdcSdjm } 3824e6f7e3dSdjm if ((o = list = strdup(_list)) == NULL) 3834e6f7e3dSdjm return -1; 3844e6f7e3dSdjm while ((cp = strsep(&list, ",")) != NULL) { 3854e6f7e3dSdjm neg = *cp == '!'; 3864e6f7e3dSdjm if (neg) 3874e6f7e3dSdjm cp++; 3884e6f7e3dSdjm if (*cp == '\0') { 389*c1f5f430Sdjm ret = -2; 3904e6f7e3dSdjm break; 3914e6f7e3dSdjm } 3924e6f7e3dSdjm /* Prefer CIDR address matching */ 3934e6f7e3dSdjm r = addr_pton_cidr(cp, &match_addr, &masklen); 3944e6f7e3dSdjm if (r == -2) { 3954e6f7e3dSdjm error("Inconsistent mask length for " 3964e6f7e3dSdjm "network \"%.100s\"", cp); 397*c1f5f430Sdjm ret = -2; 3984e6f7e3dSdjm break; 3994e6f7e3dSdjm } else if (r == 0) { 4004e6f7e3dSdjm if (addr != NULL && addr_netmatch(&try_addr, 4014e6f7e3dSdjm &match_addr, masklen) == 0) { 4024e6f7e3dSdjm foundit: 4034e6f7e3dSdjm if (neg) { 404*c1f5f430Sdjm ret = -1; 4054e6f7e3dSdjm break; 4064e6f7e3dSdjm } 4074e6f7e3dSdjm ret = 1; 4084e6f7e3dSdjm } 4094e6f7e3dSdjm continue; 4104e6f7e3dSdjm } else { 4114e6f7e3dSdjm /* If CIDR parse failed, try wildcard string match */ 4124e6f7e3dSdjm if (addr != NULL && match_pattern(addr, cp) == 1) 4134e6f7e3dSdjm goto foundit; 4144e6f7e3dSdjm } 4154e6f7e3dSdjm } 4164e6f7e3dSdjm free(o); 4174e6f7e3dSdjm 4184e6f7e3dSdjm return ret; 4194e6f7e3dSdjm } 420