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