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