/*** This file is part of PulseAudio. Copyright 2004-2006 Lennart Poettering Copyright 2006 Pierre Ossman for Cendio AB PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif #include #include #include #include #include #include #include #include "ipacl.h" struct acl_entry { PA_LLIST_FIELDS(struct acl_entry); int family; struct in_addr address_ipv4; #ifdef HAVE_IPV6 struct in6_addr address_ipv6; #endif int bits; }; struct pa_ip_acl { PA_LLIST_HEAD(struct acl_entry, entries); }; pa_ip_acl* pa_ip_acl_new(const char *s) { const char *state = NULL; char *a; pa_ip_acl *acl; pa_assert(s); acl = pa_xnew(pa_ip_acl, 1); PA_LLIST_HEAD_INIT(struct acl_entry, acl->entries); while ((a = pa_split(s, ";", &state))) { char *slash; struct acl_entry e, *n; uint32_t bits; if ((slash = strchr(a, '/'))) { *slash = 0; slash++; if (pa_atou(slash, &bits) < 0) { pa_log_warn("Failed to parse number of bits: %s", slash); goto fail; } } else bits = (uint32_t) -1; if (inet_pton(AF_INET, a, &e.address_ipv4) > 0) { e.bits = bits == (uint32_t) -1 ? 32 : (int) bits; if (e.bits > 32) { pa_log_warn("Number of bits out of range: %i", e.bits); goto fail; } e.family = AF_INET; if (e.bits < 32 && (uint32_t) (ntohl(e.address_ipv4.s_addr) << e.bits) != 0) pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits); #ifdef HAVE_IPV6 } else if (inet_pton(AF_INET6, a, &e.address_ipv6) > 0) { e.bits = bits == (uint32_t) -1 ? 128 : (int) bits; if (e.bits > 128) { pa_log_warn("Number of bits out of range: %i", e.bits); goto fail; } e.family = AF_INET6; if (e.bits < 128) { int t = 0, i; for (i = 0, bits = (uint32_t) e.bits; i < 16; i++) { if (bits >= 8) bits -= 8; else { if ((uint8_t) ((e.address_ipv6.s6_addr[i]) << bits) != 0) { t = 1; break; } bits = 0; } } if (t) pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits); } #endif } else { pa_log_warn("Failed to parse address: %s", a); goto fail; } n = pa_xmemdup(&e, sizeof(struct acl_entry)); PA_LLIST_PREPEND(struct acl_entry, acl->entries, n); pa_xfree(a); } return acl; fail: pa_xfree(a); pa_ip_acl_free(acl); return NULL; } void pa_ip_acl_free(pa_ip_acl *acl) { pa_assert(acl); while (acl->entries) { struct acl_entry *e = acl->entries; PA_LLIST_REMOVE(struct acl_entry, acl->entries, e); pa_xfree(e); } pa_xfree(acl); } int pa_ip_acl_check(pa_ip_acl *acl, int fd) { struct sockaddr_storage sa; struct acl_entry *e; socklen_t salen; pa_assert(acl); pa_assert(fd >= 0); salen = sizeof(sa); if (getpeername(fd, (struct sockaddr*) &sa, &salen) < 0) return -1; #ifdef HAVE_IPV6 if (sa.ss_family != AF_INET && sa.ss_family != AF_INET6) #else if (sa.ss_family != AF_INET) #endif return -1; if (sa.ss_family == AF_INET && salen != sizeof(struct sockaddr_in)) return -1; #ifdef HAVE_IPV6 if (sa.ss_family == AF_INET6 && salen != sizeof(struct sockaddr_in6)) return -1; #endif for (e = acl->entries; e; e = e->next) { if (e->family != sa.ss_family) continue; if (e->family == AF_INET) { struct sockaddr_in *sai = (struct sockaddr_in*) &sa; if (e->bits == 0 || /* this needs special handling because >> takes the right-hand side modulo 32 */ (ntohl(sai->sin_addr.s_addr ^ e->address_ipv4.s_addr) >> (32 - e->bits)) == 0) return 1; #ifdef HAVE_IPV6 } else if (e->family == AF_INET6) { int i, bits; struct sockaddr_in6 *sai = (struct sockaddr_in6*) &sa; if (e->bits == 128) return memcmp(&sai->sin6_addr, &e->address_ipv6, 16) == 0; if (e->bits == 0) return 1; for (i = 0, bits = e->bits; i < 16; i++) { if (bits >= 8) { if (sai->sin6_addr.s6_addr[i] != e->address_ipv6.s6_addr[i]) break; bits -= 8; } else { if ((sai->sin6_addr.s6_addr[i] ^ e->address_ipv6.s6_addr[i]) >> (8 - bits) != 0) break; bits = 0; } if (bits == 0) return 1; } #endif } } return 0; }