1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2004-2006 Lennart Poettering 5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB 6 7 PulseAudio is free software; you can redistribute it and/or modify 8 it under the terms of the GNU Lesser General Public License as 9 published by the Free Software Foundation; either version 2.1 of the 10 License, or (at your option) any later version. 11 12 PulseAudio is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Lesser General Public License for more details. 16 17 You should have received a copy of the GNU Lesser General Public 18 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 19***/ 20 21#ifdef HAVE_CONFIG_H 22#include <config.h> 23#endif 24 25#include <sys/types.h> 26#include <sys/types.h> 27#include <string.h> 28 29#ifdef HAVE_NETINET_IN_H 30#include <netinet/in.h> 31#endif 32#ifdef HAVE_NETINET_IN_SYSTM_H 33#include <netinet/in_systm.h> 34#endif 35#ifdef HAVE_NETINET_IP_H 36#include <netinet/ip.h> 37#endif 38 39#include <pulse/xmalloc.h> 40 41#include <pulsecore/core-util.h> 42#include <pulsecore/llist.h> 43#include <pulsecore/log.h> 44#include <pulsecore/macro.h> 45#include <pulsecore/socket.h> 46#include <pulsecore/arpa-inet.h> 47 48#include "ipacl.h" 49 50struct acl_entry { 51 PA_LLIST_FIELDS(struct acl_entry); 52 int family; 53 struct in_addr address_ipv4; 54#ifdef HAVE_IPV6 55 struct in6_addr address_ipv6; 56#endif 57 int bits; 58}; 59 60struct pa_ip_acl { 61 PA_LLIST_HEAD(struct acl_entry, entries); 62}; 63 64pa_ip_acl* pa_ip_acl_new(const char *s) { 65 const char *state = NULL; 66 char *a; 67 pa_ip_acl *acl; 68 69 pa_assert(s); 70 71 acl = pa_xnew(pa_ip_acl, 1); 72 PA_LLIST_HEAD_INIT(struct acl_entry, acl->entries); 73 74 while ((a = pa_split(s, ";", &state))) { 75 char *slash; 76 struct acl_entry e, *n; 77 uint32_t bits; 78 79 if ((slash = strchr(a, '/'))) { 80 *slash = 0; 81 slash++; 82 if (pa_atou(slash, &bits) < 0) { 83 pa_log_warn("Failed to parse number of bits: %s", slash); 84 goto fail; 85 } 86 } else 87 bits = (uint32_t) -1; 88 89 if (inet_pton(AF_INET, a, &e.address_ipv4) > 0) { 90 91 e.bits = bits == (uint32_t) -1 ? 32 : (int) bits; 92 93 if (e.bits > 32) { 94 pa_log_warn("Number of bits out of range: %i", e.bits); 95 goto fail; 96 } 97 98 e.family = AF_INET; 99 100 if (e.bits < 32 && (uint32_t) (ntohl(e.address_ipv4.s_addr) << e.bits) != 0) 101 pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits); 102 103#ifdef HAVE_IPV6 104 } else if (inet_pton(AF_INET6, a, &e.address_ipv6) > 0) { 105 106 e.bits = bits == (uint32_t) -1 ? 128 : (int) bits; 107 108 if (e.bits > 128) { 109 pa_log_warn("Number of bits out of range: %i", e.bits); 110 goto fail; 111 } 112 e.family = AF_INET6; 113 114 if (e.bits < 128) { 115 int t = 0, i; 116 117 for (i = 0, bits = (uint32_t) e.bits; i < 16; i++) { 118 119 if (bits >= 8) 120 bits -= 8; 121 else { 122 if ((uint8_t) ((e.address_ipv6.s6_addr[i]) << bits) != 0) { 123 t = 1; 124 break; 125 } 126 bits = 0; 127 } 128 } 129 130 if (t) 131 pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits); 132 } 133#endif 134 135 } else { 136 pa_log_warn("Failed to parse address: %s", a); 137 goto fail; 138 } 139 140 n = pa_xmemdup(&e, sizeof(struct acl_entry)); 141 PA_LLIST_PREPEND(struct acl_entry, acl->entries, n); 142 143 pa_xfree(a); 144 } 145 146 return acl; 147 148fail: 149 pa_xfree(a); 150 pa_ip_acl_free(acl); 151 152 return NULL; 153} 154 155void pa_ip_acl_free(pa_ip_acl *acl) { 156 pa_assert(acl); 157 158 while (acl->entries) { 159 struct acl_entry *e = acl->entries; 160 PA_LLIST_REMOVE(struct acl_entry, acl->entries, e); 161 pa_xfree(e); 162 } 163 164 pa_xfree(acl); 165} 166 167int pa_ip_acl_check(pa_ip_acl *acl, int fd) { 168 struct sockaddr_storage sa; 169 struct acl_entry *e; 170 socklen_t salen; 171 172 pa_assert(acl); 173 pa_assert(fd >= 0); 174 175 salen = sizeof(sa); 176 if (getpeername(fd, (struct sockaddr*) &sa, &salen) < 0) 177 return -1; 178 179#ifdef HAVE_IPV6 180 if (sa.ss_family != AF_INET && sa.ss_family != AF_INET6) 181#else 182 if (sa.ss_family != AF_INET) 183#endif 184 return -1; 185 186 if (sa.ss_family == AF_INET && salen != sizeof(struct sockaddr_in)) 187 return -1; 188 189#ifdef HAVE_IPV6 190 if (sa.ss_family == AF_INET6 && salen != sizeof(struct sockaddr_in6)) 191 return -1; 192#endif 193 194 for (e = acl->entries; e; e = e->next) { 195 196 if (e->family != sa.ss_family) 197 continue; 198 199 if (e->family == AF_INET) { 200 struct sockaddr_in *sai = (struct sockaddr_in*) &sa; 201 202 if (e->bits == 0 || /* this needs special handling because >> takes the right-hand side modulo 32 */ 203 (ntohl(sai->sin_addr.s_addr ^ e->address_ipv4.s_addr) >> (32 - e->bits)) == 0) 204 return 1; 205#ifdef HAVE_IPV6 206 } else if (e->family == AF_INET6) { 207 int i, bits; 208 struct sockaddr_in6 *sai = (struct sockaddr_in6*) &sa; 209 210 if (e->bits == 128) 211 return memcmp(&sai->sin6_addr, &e->address_ipv6, 16) == 0; 212 213 if (e->bits == 0) 214 return 1; 215 216 for (i = 0, bits = e->bits; i < 16; i++) { 217 218 if (bits >= 8) { 219 if (sai->sin6_addr.s6_addr[i] != e->address_ipv6.s6_addr[i]) 220 break; 221 222 bits -= 8; 223 } else { 224 if ((sai->sin6_addr.s6_addr[i] ^ e->address_ipv6.s6_addr[i]) >> (8 - bits) != 0) 225 break; 226 227 bits = 0; 228 } 229 230 if (bits == 0) 231 return 1; 232 } 233#endif 234 } 235 } 236 237 return 0; 238} 239