1*b94e498eSdjm /* $OpenBSD: addrmatch.c,v 1.5 2010/02/26 20:29:54 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" 323c643052Sstevesk #include "xmalloc.h" 334e6f7e3dSdjm 344e6f7e3dSdjm struct xaddr { 354e6f7e3dSdjm sa_family_t af; 364e6f7e3dSdjm union { 374e6f7e3dSdjm struct in_addr v4; 384e6f7e3dSdjm struct in6_addr v6; 394e6f7e3dSdjm u_int8_t addr8[16]; 404e6f7e3dSdjm u_int32_t addr32[4]; 414e6f7e3dSdjm } xa; /* 128-bit address */ 424e6f7e3dSdjm u_int32_t scope_id; /* iface scope id for v6 */ 434e6f7e3dSdjm #define v4 xa.v4 444e6f7e3dSdjm #define v6 xa.v6 454e6f7e3dSdjm #define addr8 xa.addr8 464e6f7e3dSdjm #define addr32 xa.addr32 474e6f7e3dSdjm }; 484e6f7e3dSdjm 494e6f7e3dSdjm static int 504e6f7e3dSdjm addr_unicast_masklen(int af) 514e6f7e3dSdjm { 524e6f7e3dSdjm switch (af) { 534e6f7e3dSdjm case AF_INET: 544e6f7e3dSdjm return 32; 554e6f7e3dSdjm case AF_INET6: 564e6f7e3dSdjm return 128; 574e6f7e3dSdjm default: 584e6f7e3dSdjm return -1; 594e6f7e3dSdjm } 604e6f7e3dSdjm } 614e6f7e3dSdjm 624e6f7e3dSdjm static inline int 634e6f7e3dSdjm masklen_valid(int af, u_int masklen) 644e6f7e3dSdjm { 654e6f7e3dSdjm switch (af) { 664e6f7e3dSdjm case AF_INET: 674e6f7e3dSdjm return masklen <= 32 ? 0 : -1; 684e6f7e3dSdjm case AF_INET6: 694e6f7e3dSdjm return masklen <= 128 ? 0 : -1; 704e6f7e3dSdjm default: 714e6f7e3dSdjm return -1; 724e6f7e3dSdjm } 734e6f7e3dSdjm } 744e6f7e3dSdjm 754e6f7e3dSdjm /* 764e6f7e3dSdjm * Convert struct sockaddr to struct xaddr 774e6f7e3dSdjm * Returns 0 on success, -1 on failure. 784e6f7e3dSdjm */ 794e6f7e3dSdjm static int 804e6f7e3dSdjm addr_sa_to_xaddr(struct sockaddr *sa, socklen_t slen, struct xaddr *xa) 814e6f7e3dSdjm { 824e6f7e3dSdjm struct sockaddr_in *in4 = (struct sockaddr_in *)sa; 834e6f7e3dSdjm struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa; 844e6f7e3dSdjm 854e6f7e3dSdjm memset(xa, '\0', sizeof(*xa)); 864e6f7e3dSdjm 874e6f7e3dSdjm switch (sa->sa_family) { 884e6f7e3dSdjm case AF_INET: 894e6f7e3dSdjm if (slen < sizeof(*in4)) 904e6f7e3dSdjm return -1; 914e6f7e3dSdjm xa->af = AF_INET; 924e6f7e3dSdjm memcpy(&xa->v4, &in4->sin_addr, sizeof(xa->v4)); 934e6f7e3dSdjm break; 944e6f7e3dSdjm case AF_INET6: 954e6f7e3dSdjm if (slen < sizeof(*in6)) 964e6f7e3dSdjm return -1; 974e6f7e3dSdjm xa->af = AF_INET6; 984e6f7e3dSdjm memcpy(&xa->v6, &in6->sin6_addr, sizeof(xa->v6)); 994e6f7e3dSdjm xa->scope_id = in6->sin6_scope_id; 1004e6f7e3dSdjm break; 1014e6f7e3dSdjm default: 1024e6f7e3dSdjm return -1; 1034e6f7e3dSdjm } 1044e6f7e3dSdjm 1054e6f7e3dSdjm return 0; 1064e6f7e3dSdjm } 1074e6f7e3dSdjm 1084e6f7e3dSdjm /* 1094e6f7e3dSdjm * Calculate a netmask of length 'l' for address family 'af' and 1104e6f7e3dSdjm * store it in 'n'. 1114e6f7e3dSdjm * Returns 0 on success, -1 on failure. 1124e6f7e3dSdjm */ 1134e6f7e3dSdjm static int 1144e6f7e3dSdjm addr_netmask(int af, u_int l, struct xaddr *n) 1154e6f7e3dSdjm { 1164e6f7e3dSdjm int i; 1174e6f7e3dSdjm 1184e6f7e3dSdjm if (masklen_valid(af, l) != 0 || n == NULL) 1194e6f7e3dSdjm return -1; 1204e6f7e3dSdjm 1214e6f7e3dSdjm memset(n, '\0', sizeof(*n)); 1224e6f7e3dSdjm switch (af) { 1234e6f7e3dSdjm case AF_INET: 1244e6f7e3dSdjm n->af = AF_INET; 125*b94e498eSdjm if (l == 0) 126*b94e498eSdjm return 0; 1274e6f7e3dSdjm n->v4.s_addr = htonl((0xffffffff << (32 - l)) & 0xffffffff); 1284e6f7e3dSdjm return 0; 1294e6f7e3dSdjm case AF_INET6: 1304e6f7e3dSdjm n->af = AF_INET6; 1314e6f7e3dSdjm for (i = 0; i < 4 && l >= 32; i++, l -= 32) 1324e6f7e3dSdjm n->addr32[i] = 0xffffffffU; 1334e6f7e3dSdjm if (i < 4 && l != 0) 1344e6f7e3dSdjm n->addr32[i] = htonl((0xffffffff << (32 - l)) & 1354e6f7e3dSdjm 0xffffffff); 1364e6f7e3dSdjm return 0; 1374e6f7e3dSdjm default: 1384e6f7e3dSdjm return -1; 1394e6f7e3dSdjm } 1404e6f7e3dSdjm } 1414e6f7e3dSdjm 1424e6f7e3dSdjm /* 1434e6f7e3dSdjm * Perform logical AND of addresses 'a' and 'b', storing result in 'dst'. 1444e6f7e3dSdjm * Returns 0 on success, -1 on failure. 1454e6f7e3dSdjm */ 1464e6f7e3dSdjm static int 1474e6f7e3dSdjm addr_and(struct xaddr *dst, const struct xaddr *a, const struct xaddr *b) 1484e6f7e3dSdjm { 1494e6f7e3dSdjm int i; 1504e6f7e3dSdjm 1514e6f7e3dSdjm if (dst == NULL || a == NULL || b == NULL || a->af != b->af) 1524e6f7e3dSdjm return -1; 1534e6f7e3dSdjm 1544e6f7e3dSdjm memcpy(dst, a, sizeof(*dst)); 1554e6f7e3dSdjm switch (a->af) { 1564e6f7e3dSdjm case AF_INET: 1574e6f7e3dSdjm dst->v4.s_addr &= b->v4.s_addr; 1584e6f7e3dSdjm return 0; 1594e6f7e3dSdjm case AF_INET6: 1604e6f7e3dSdjm dst->scope_id = a->scope_id; 1614e6f7e3dSdjm for (i = 0; i < 4; i++) 1624e6f7e3dSdjm dst->addr32[i] &= b->addr32[i]; 1634e6f7e3dSdjm return 0; 1644e6f7e3dSdjm default: 1654e6f7e3dSdjm return -1; 1664e6f7e3dSdjm } 1674e6f7e3dSdjm } 1684e6f7e3dSdjm 1694e6f7e3dSdjm /* 1704e6f7e3dSdjm * Compare addresses 'a' and 'b' 1714e6f7e3dSdjm * Return 0 if addresses are identical, -1 if (a < b) or 1 if (a > b) 1724e6f7e3dSdjm */ 1734e6f7e3dSdjm static int 1744e6f7e3dSdjm addr_cmp(const struct xaddr *a, const struct xaddr *b) 1754e6f7e3dSdjm { 1764e6f7e3dSdjm int i; 1774e6f7e3dSdjm 1784e6f7e3dSdjm if (a->af != b->af) 1794e6f7e3dSdjm return a->af == AF_INET6 ? 1 : -1; 1804e6f7e3dSdjm 1814e6f7e3dSdjm switch (a->af) { 1824e6f7e3dSdjm case AF_INET: 1834e6f7e3dSdjm if (a->v4.s_addr == b->v4.s_addr) 1844e6f7e3dSdjm return 0; 1854e6f7e3dSdjm return ntohl(a->v4.s_addr) > ntohl(b->v4.s_addr) ? 1 : -1; 1864e6f7e3dSdjm case AF_INET6: 1874e6f7e3dSdjm for (i = 0; i < 16; i++) 1884e6f7e3dSdjm if (a->addr8[i] - b->addr8[i] != 0) 1894e6f7e3dSdjm return a->addr8[i] > b->addr8[i] ? 1 : -1; 1904e6f7e3dSdjm if (a->scope_id == b->scope_id) 1914e6f7e3dSdjm return 0; 1924e6f7e3dSdjm return a->scope_id > b->scope_id ? 1 : -1; 1934e6f7e3dSdjm default: 1944e6f7e3dSdjm return -1; 1954e6f7e3dSdjm } 1964e6f7e3dSdjm } 1974e6f7e3dSdjm 1984e6f7e3dSdjm /* 1994e6f7e3dSdjm * Parse string address 'p' into 'n' 2004e6f7e3dSdjm * Returns 0 on success, -1 on failure. 2014e6f7e3dSdjm */ 2024e6f7e3dSdjm static int 2034e6f7e3dSdjm addr_pton(const char *p, struct xaddr *n) 2044e6f7e3dSdjm { 2054e6f7e3dSdjm struct addrinfo hints, *ai; 2064e6f7e3dSdjm 2074e6f7e3dSdjm memset(&hints, '\0', sizeof(hints)); 2084e6f7e3dSdjm hints.ai_flags = AI_NUMERICHOST; 2094e6f7e3dSdjm 2104e6f7e3dSdjm if (p == NULL || getaddrinfo(p, NULL, &hints, &ai) != 0) 2114e6f7e3dSdjm return -1; 2124e6f7e3dSdjm 2134e6f7e3dSdjm if (ai == NULL || ai->ai_addr == NULL) 2144e6f7e3dSdjm return -1; 2154e6f7e3dSdjm 2164e6f7e3dSdjm if (n != NULL && 2174e6f7e3dSdjm addr_sa_to_xaddr(ai->ai_addr, ai->ai_addrlen, n) == -1) { 2184e6f7e3dSdjm freeaddrinfo(ai); 2194e6f7e3dSdjm return -1; 2204e6f7e3dSdjm } 2214e6f7e3dSdjm 2224e6f7e3dSdjm freeaddrinfo(ai); 2234e6f7e3dSdjm return 0; 2244e6f7e3dSdjm } 2254e6f7e3dSdjm 2264e6f7e3dSdjm /* 2274e6f7e3dSdjm * Perform bitwise negation of address 2284e6f7e3dSdjm * Returns 0 on success, -1 on failure. 2294e6f7e3dSdjm */ 2304e6f7e3dSdjm static int 2314e6f7e3dSdjm addr_invert(struct xaddr *n) 2324e6f7e3dSdjm { 2334e6f7e3dSdjm int i; 2344e6f7e3dSdjm 2354e6f7e3dSdjm if (n == NULL) 2364e6f7e3dSdjm return (-1); 2374e6f7e3dSdjm 2384e6f7e3dSdjm switch (n->af) { 2394e6f7e3dSdjm case AF_INET: 2404e6f7e3dSdjm n->v4.s_addr = ~n->v4.s_addr; 2414e6f7e3dSdjm return (0); 2424e6f7e3dSdjm case AF_INET6: 2434e6f7e3dSdjm for (i = 0; i < 4; i++) 2444e6f7e3dSdjm n->addr32[i] = ~n->addr32[i]; 2454e6f7e3dSdjm return (0); 2464e6f7e3dSdjm default: 2474e6f7e3dSdjm return (-1); 2484e6f7e3dSdjm } 2494e6f7e3dSdjm } 2504e6f7e3dSdjm 2514e6f7e3dSdjm /* 2524e6f7e3dSdjm * Calculate a netmask of length 'l' for address family 'af' and 2534e6f7e3dSdjm * store it in 'n'. 2544e6f7e3dSdjm * Returns 0 on success, -1 on failure. 2554e6f7e3dSdjm */ 2564e6f7e3dSdjm static int 2574e6f7e3dSdjm addr_hostmask(int af, u_int l, struct xaddr *n) 2584e6f7e3dSdjm { 2594e6f7e3dSdjm if (addr_netmask(af, l, n) == -1 || addr_invert(n) == -1) 2604e6f7e3dSdjm return (-1); 2614e6f7e3dSdjm return (0); 2624e6f7e3dSdjm } 2634e6f7e3dSdjm 2644e6f7e3dSdjm /* 2654e6f7e3dSdjm * Test whether address 'a' is all zeros (i.e. 0.0.0.0 or ::) 2664e6f7e3dSdjm * Returns 0 on if address is all-zeros, -1 if not all zeros or on failure. 2674e6f7e3dSdjm */ 2684e6f7e3dSdjm static int 2694e6f7e3dSdjm addr_is_all0s(const struct xaddr *a) 2704e6f7e3dSdjm { 2714e6f7e3dSdjm int i; 2724e6f7e3dSdjm 2734e6f7e3dSdjm switch (a->af) { 2744e6f7e3dSdjm case AF_INET: 2754e6f7e3dSdjm return (a->v4.s_addr == 0 ? 0 : -1); 2764e6f7e3dSdjm case AF_INET6:; 2774e6f7e3dSdjm for (i = 0; i < 4; i++) 2784e6f7e3dSdjm if (a->addr32[i] != 0) 2794e6f7e3dSdjm return (-1); 2804e6f7e3dSdjm return (0); 2814e6f7e3dSdjm default: 2824e6f7e3dSdjm return (-1); 2834e6f7e3dSdjm } 2844e6f7e3dSdjm } 2854e6f7e3dSdjm 2864e6f7e3dSdjm /* 2874e6f7e3dSdjm * Test whether host portion of address 'a', as determined by 'masklen' 2884e6f7e3dSdjm * is all zeros. 2894e6f7e3dSdjm * Returns 0 on if host portion of address is all-zeros, 2904e6f7e3dSdjm * -1 if not all zeros or on failure. 2914e6f7e3dSdjm */ 2924e6f7e3dSdjm static int 2934e6f7e3dSdjm addr_host_is_all0s(const struct xaddr *a, u_int masklen) 2944e6f7e3dSdjm { 2954e6f7e3dSdjm struct xaddr tmp_addr, tmp_mask, tmp_result; 2964e6f7e3dSdjm 2974e6f7e3dSdjm memcpy(&tmp_addr, a, sizeof(tmp_addr)); 2984e6f7e3dSdjm if (addr_hostmask(a->af, masklen, &tmp_mask) == -1) 2994e6f7e3dSdjm return (-1); 3004e6f7e3dSdjm if (addr_and(&tmp_result, &tmp_addr, &tmp_mask) == -1) 3014e6f7e3dSdjm return (-1); 3024e6f7e3dSdjm return (addr_is_all0s(&tmp_result)); 3034e6f7e3dSdjm } 3044e6f7e3dSdjm 3054e6f7e3dSdjm /* 3064e6f7e3dSdjm * Parse a CIDR address (x.x.x.x/y or xxxx:yyyy::/z). 3074e6f7e3dSdjm * Return -1 on parse error, -2 on inconsistency or 0 on success. 3084e6f7e3dSdjm */ 3094e6f7e3dSdjm static int 3104e6f7e3dSdjm addr_pton_cidr(const char *p, struct xaddr *n, u_int *l) 3114e6f7e3dSdjm { 3124e6f7e3dSdjm struct xaddr tmp; 3134e6f7e3dSdjm long unsigned int masklen = 999; 3144e6f7e3dSdjm char addrbuf[64], *mp, *cp; 3154e6f7e3dSdjm 3164e6f7e3dSdjm /* Don't modify argument */ 3174e6f7e3dSdjm if (p == NULL || strlcpy(addrbuf, p, sizeof(addrbuf)) > sizeof(addrbuf)) 3184e6f7e3dSdjm return -1; 3194e6f7e3dSdjm 3204e6f7e3dSdjm if ((mp = strchr(addrbuf, '/')) != NULL) { 3214e6f7e3dSdjm *mp = '\0'; 3224e6f7e3dSdjm mp++; 3234e6f7e3dSdjm masklen = strtoul(mp, &cp, 10); 3244e6f7e3dSdjm if (*mp == '\0' || *cp != '\0' || masklen > 128) 3254e6f7e3dSdjm return -1; 3264e6f7e3dSdjm } 3274e6f7e3dSdjm 3284e6f7e3dSdjm if (addr_pton(addrbuf, &tmp) == -1) 3294e6f7e3dSdjm return -1; 3304e6f7e3dSdjm 3314e6f7e3dSdjm if (mp == NULL) 3324e6f7e3dSdjm masklen = addr_unicast_masklen(tmp.af); 3334e6f7e3dSdjm if (masklen_valid(tmp.af, masklen) == -1) 3344e6f7e3dSdjm return -2; 3354e6f7e3dSdjm if (addr_host_is_all0s(&tmp, masklen) != 0) 3364e6f7e3dSdjm return -2; 3374e6f7e3dSdjm 3384e6f7e3dSdjm if (n != NULL) 3394e6f7e3dSdjm memcpy(n, &tmp, sizeof(*n)); 3404e6f7e3dSdjm if (l != NULL) 3414e6f7e3dSdjm *l = masklen; 3424e6f7e3dSdjm 3434e6f7e3dSdjm return 0; 3444e6f7e3dSdjm } 3454e6f7e3dSdjm 3464e6f7e3dSdjm static int 3474e6f7e3dSdjm addr_netmatch(const struct xaddr *host, const struct xaddr *net, u_int masklen) 3484e6f7e3dSdjm { 3494e6f7e3dSdjm struct xaddr tmp_mask, tmp_result; 3504e6f7e3dSdjm 3514e6f7e3dSdjm if (host->af != net->af) 3524e6f7e3dSdjm return -1; 3534e6f7e3dSdjm 3544e6f7e3dSdjm if (addr_netmask(host->af, masklen, &tmp_mask) == -1) 3554e6f7e3dSdjm return -1; 3564e6f7e3dSdjm if (addr_and(&tmp_result, host, &tmp_mask) == -1) 3574e6f7e3dSdjm return -1; 3584e6f7e3dSdjm return addr_cmp(&tmp_result, net); 3594e6f7e3dSdjm } 3604e6f7e3dSdjm 3614e6f7e3dSdjm /* 3624e6f7e3dSdjm * Match "addr" against list pattern list "_list", which may contain a 3634e6f7e3dSdjm * mix of CIDR addresses and old-school wildcards. 3644e6f7e3dSdjm * 3654e6f7e3dSdjm * If addr is NULL, then no matching is performed, but _list is parsed 3664e6f7e3dSdjm * and checked for well-formedness. 3674e6f7e3dSdjm * 3684e6f7e3dSdjm * Returns 1 on match found (never returned when addr == NULL). 3694e6f7e3dSdjm * Returns 0 on if no match found, or no errors found when addr == NULL. 370c1f5f430Sdjm * Returns -1 on negated match found (never returned when addr == NULL). 371c1f5f430Sdjm * Returns -2 on invalid list entry. 3724e6f7e3dSdjm */ 3734e6f7e3dSdjm int 3744e6f7e3dSdjm addr_match_list(const char *addr, const char *_list) 3754e6f7e3dSdjm { 3764e6f7e3dSdjm char *list, *cp, *o; 3774e6f7e3dSdjm struct xaddr try_addr, match_addr; 3784e6f7e3dSdjm u_int masklen, neg; 3794e6f7e3dSdjm int ret = 0, r; 3804e6f7e3dSdjm 381952f5cdcSdjm if (addr != NULL && addr_pton(addr, &try_addr) != 0) { 382952f5cdcSdjm debug2("%s: couldn't parse address %.100s", __func__, addr); 383952f5cdcSdjm return 0; 384952f5cdcSdjm } 3854e6f7e3dSdjm if ((o = list = strdup(_list)) == NULL) 3864e6f7e3dSdjm return -1; 3874e6f7e3dSdjm while ((cp = strsep(&list, ",")) != NULL) { 3884e6f7e3dSdjm neg = *cp == '!'; 3894e6f7e3dSdjm if (neg) 3904e6f7e3dSdjm cp++; 3914e6f7e3dSdjm if (*cp == '\0') { 392c1f5f430Sdjm ret = -2; 3934e6f7e3dSdjm break; 3944e6f7e3dSdjm } 3954e6f7e3dSdjm /* Prefer CIDR address matching */ 3964e6f7e3dSdjm r = addr_pton_cidr(cp, &match_addr, &masklen); 3974e6f7e3dSdjm if (r == -2) { 3984e6f7e3dSdjm error("Inconsistent mask length for " 3994e6f7e3dSdjm "network \"%.100s\"", cp); 400c1f5f430Sdjm ret = -2; 4014e6f7e3dSdjm break; 4024e6f7e3dSdjm } else if (r == 0) { 4034e6f7e3dSdjm if (addr != NULL && addr_netmatch(&try_addr, 4044e6f7e3dSdjm &match_addr, masklen) == 0) { 4054e6f7e3dSdjm foundit: 4064e6f7e3dSdjm if (neg) { 407c1f5f430Sdjm ret = -1; 4084e6f7e3dSdjm break; 4094e6f7e3dSdjm } 4104e6f7e3dSdjm ret = 1; 4114e6f7e3dSdjm } 4124e6f7e3dSdjm continue; 4134e6f7e3dSdjm } else { 4144e6f7e3dSdjm /* If CIDR parse failed, try wildcard string match */ 4154e6f7e3dSdjm if (addr != NULL && match_pattern(addr, cp) == 1) 4164e6f7e3dSdjm goto foundit; 4174e6f7e3dSdjm } 4184e6f7e3dSdjm } 4193c643052Sstevesk xfree(o); 4204e6f7e3dSdjm 4214e6f7e3dSdjm return ret; 4224e6f7e3dSdjm } 423*b94e498eSdjm 424*b94e498eSdjm /* 425*b94e498eSdjm * Match "addr" against list CIDR list "_list". Lexical wildcards and 426*b94e498eSdjm * negation are not supported. If "addr" == NULL, will verify structure 427*b94e498eSdjm * of "_list". 428*b94e498eSdjm * 429*b94e498eSdjm * Returns 1 on match found (never returned when addr == NULL). 430*b94e498eSdjm * Returns 0 on if no match found, or no errors found when addr == NULL. 431*b94e498eSdjm * Returns -1 on error 432*b94e498eSdjm */ 433*b94e498eSdjm int 434*b94e498eSdjm addr_match_cidr_list(const char *addr, const char *_list) 435*b94e498eSdjm { 436*b94e498eSdjm char *list, *cp, *o; 437*b94e498eSdjm struct xaddr try_addr, match_addr; 438*b94e498eSdjm u_int masklen; 439*b94e498eSdjm int ret = 0, r; 440*b94e498eSdjm 441*b94e498eSdjm if (addr != NULL && addr_pton(addr, &try_addr) != 0) { 442*b94e498eSdjm debug2("%s: couldn't parse address %.100s", __func__, addr); 443*b94e498eSdjm return 0; 444*b94e498eSdjm } 445*b94e498eSdjm if ((o = list = strdup(_list)) == NULL) 446*b94e498eSdjm return -1; 447*b94e498eSdjm while ((cp = strsep(&list, ",")) != NULL) { 448*b94e498eSdjm if (*cp == '\0') { 449*b94e498eSdjm error("%s: empty entry in list \"%.100s\"", 450*b94e498eSdjm __func__, o); 451*b94e498eSdjm ret = -1; 452*b94e498eSdjm break; 453*b94e498eSdjm } 454*b94e498eSdjm 455*b94e498eSdjm /* 456*b94e498eSdjm * NB. This function is called in pre-auth with untrusted data, 457*b94e498eSdjm * so be extra paranoid about junk reaching getaddrino (via 458*b94e498eSdjm * addr_pton_cidr). 459*b94e498eSdjm */ 460*b94e498eSdjm 461*b94e498eSdjm /* Stop junk from reaching getaddrinfo. +3 is for masklen */ 462*b94e498eSdjm if (strlen(cp) > INET6_ADDRSTRLEN + 3) { 463*b94e498eSdjm error("%s: list entry \"%.100s\" too long", 464*b94e498eSdjm __func__, cp); 465*b94e498eSdjm ret = -1; 466*b94e498eSdjm break; 467*b94e498eSdjm } 468*b94e498eSdjm #define VALID_CIDR_CHARS "0123456789abcdefABCDEF.:/" 469*b94e498eSdjm if (strspn(cp, VALID_CIDR_CHARS) != strlen(cp)) { 470*b94e498eSdjm error("%s: list entry \"%.100s\" contains invalid " 471*b94e498eSdjm "characters", __func__, cp); 472*b94e498eSdjm ret = -1; 473*b94e498eSdjm } 474*b94e498eSdjm 475*b94e498eSdjm /* Prefer CIDR address matching */ 476*b94e498eSdjm r = addr_pton_cidr(cp, &match_addr, &masklen); 477*b94e498eSdjm if (r == -1) { 478*b94e498eSdjm error("Invalid network entry \"%.100s\"", cp); 479*b94e498eSdjm ret = -1; 480*b94e498eSdjm break; 481*b94e498eSdjm } else if (r == -2) { 482*b94e498eSdjm error("Inconsistent mask length for " 483*b94e498eSdjm "network \"%.100s\"", cp); 484*b94e498eSdjm ret = -1; 485*b94e498eSdjm break; 486*b94e498eSdjm } else if (r == 0 && addr != NULL) { 487*b94e498eSdjm if (addr_netmatch(&try_addr, &match_addr, 488*b94e498eSdjm masklen) == 0) 489*b94e498eSdjm ret = 1; 490*b94e498eSdjm continue; 491*b94e498eSdjm } 492*b94e498eSdjm } 493*b94e498eSdjm xfree(o); 494*b94e498eSdjm 495*b94e498eSdjm return ret; 496*b94e498eSdjm } 497