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