• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
50 struct 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 
60 struct pa_ip_acl {
61     PA_LLIST_HEAD(struct acl_entry, entries);
62 };
63 
pa_ip_acl_new(const char * s)64 pa_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 
148 fail:
149     pa_xfree(a);
150     pa_ip_acl_free(acl);
151 
152     return NULL;
153 }
154 
pa_ip_acl_free(pa_ip_acl * acl)155 void 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 
pa_ip_acl_check(pa_ip_acl * acl,int fd)167 int 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