153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2004-2006 Lennart Poettering
553a5a1b3Sopenharmony_ci  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
653a5a1b3Sopenharmony_ci
753a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
853a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as
953a5a1b3Sopenharmony_ci  published by the Free Software Foundation; either version 2.1 of the
1053a5a1b3Sopenharmony_ci  License, or (at your option) any later version.
1153a5a1b3Sopenharmony_ci
1253a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1353a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1453a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1553a5a1b3Sopenharmony_ci  Lesser General Public License for more details.
1653a5a1b3Sopenharmony_ci
1753a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public
1853a5a1b3Sopenharmony_ci  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1953a5a1b3Sopenharmony_ci***/
2053a5a1b3Sopenharmony_ci
2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2253a5a1b3Sopenharmony_ci#include <config.h>
2353a5a1b3Sopenharmony_ci#endif
2453a5a1b3Sopenharmony_ci
2553a5a1b3Sopenharmony_ci#include <sys/types.h>
2653a5a1b3Sopenharmony_ci#include <sys/types.h>
2753a5a1b3Sopenharmony_ci#include <string.h>
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
3053a5a1b3Sopenharmony_ci#include <netinet/in.h>
3153a5a1b3Sopenharmony_ci#endif
3253a5a1b3Sopenharmony_ci#ifdef HAVE_NETINET_IN_SYSTM_H
3353a5a1b3Sopenharmony_ci#include <netinet/in_systm.h>
3453a5a1b3Sopenharmony_ci#endif
3553a5a1b3Sopenharmony_ci#ifdef HAVE_NETINET_IP_H
3653a5a1b3Sopenharmony_ci#include <netinet/ip.h>
3753a5a1b3Sopenharmony_ci#endif
3853a5a1b3Sopenharmony_ci
3953a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
4053a5a1b3Sopenharmony_ci
4153a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
4253a5a1b3Sopenharmony_ci#include <pulsecore/llist.h>
4353a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
4453a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
4553a5a1b3Sopenharmony_ci#include <pulsecore/socket.h>
4653a5a1b3Sopenharmony_ci#include <pulsecore/arpa-inet.h>
4753a5a1b3Sopenharmony_ci
4853a5a1b3Sopenharmony_ci#include "ipacl.h"
4953a5a1b3Sopenharmony_ci
5053a5a1b3Sopenharmony_cistruct acl_entry {
5153a5a1b3Sopenharmony_ci    PA_LLIST_FIELDS(struct acl_entry);
5253a5a1b3Sopenharmony_ci    int family;
5353a5a1b3Sopenharmony_ci    struct in_addr address_ipv4;
5453a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
5553a5a1b3Sopenharmony_ci    struct in6_addr address_ipv6;
5653a5a1b3Sopenharmony_ci#endif
5753a5a1b3Sopenharmony_ci    int bits;
5853a5a1b3Sopenharmony_ci};
5953a5a1b3Sopenharmony_ci
6053a5a1b3Sopenharmony_cistruct pa_ip_acl {
6153a5a1b3Sopenharmony_ci    PA_LLIST_HEAD(struct acl_entry, entries);
6253a5a1b3Sopenharmony_ci};
6353a5a1b3Sopenharmony_ci
6453a5a1b3Sopenharmony_cipa_ip_acl* pa_ip_acl_new(const char *s) {
6553a5a1b3Sopenharmony_ci    const char *state = NULL;
6653a5a1b3Sopenharmony_ci    char *a;
6753a5a1b3Sopenharmony_ci    pa_ip_acl *acl;
6853a5a1b3Sopenharmony_ci
6953a5a1b3Sopenharmony_ci    pa_assert(s);
7053a5a1b3Sopenharmony_ci
7153a5a1b3Sopenharmony_ci    acl = pa_xnew(pa_ip_acl, 1);
7253a5a1b3Sopenharmony_ci    PA_LLIST_HEAD_INIT(struct acl_entry, acl->entries);
7353a5a1b3Sopenharmony_ci
7453a5a1b3Sopenharmony_ci    while ((a = pa_split(s, ";", &state))) {
7553a5a1b3Sopenharmony_ci        char *slash;
7653a5a1b3Sopenharmony_ci        struct acl_entry e, *n;
7753a5a1b3Sopenharmony_ci        uint32_t bits;
7853a5a1b3Sopenharmony_ci
7953a5a1b3Sopenharmony_ci        if ((slash = strchr(a, '/'))) {
8053a5a1b3Sopenharmony_ci            *slash = 0;
8153a5a1b3Sopenharmony_ci            slash++;
8253a5a1b3Sopenharmony_ci            if (pa_atou(slash, &bits) < 0) {
8353a5a1b3Sopenharmony_ci                pa_log_warn("Failed to parse number of bits: %s", slash);
8453a5a1b3Sopenharmony_ci                goto fail;
8553a5a1b3Sopenharmony_ci            }
8653a5a1b3Sopenharmony_ci        } else
8753a5a1b3Sopenharmony_ci            bits = (uint32_t) -1;
8853a5a1b3Sopenharmony_ci
8953a5a1b3Sopenharmony_ci        if (inet_pton(AF_INET, a, &e.address_ipv4) > 0) {
9053a5a1b3Sopenharmony_ci
9153a5a1b3Sopenharmony_ci            e.bits = bits == (uint32_t) -1 ? 32 : (int) bits;
9253a5a1b3Sopenharmony_ci
9353a5a1b3Sopenharmony_ci            if (e.bits > 32) {
9453a5a1b3Sopenharmony_ci                pa_log_warn("Number of bits out of range: %i", e.bits);
9553a5a1b3Sopenharmony_ci                goto fail;
9653a5a1b3Sopenharmony_ci            }
9753a5a1b3Sopenharmony_ci
9853a5a1b3Sopenharmony_ci            e.family = AF_INET;
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_ci            if (e.bits < 32 && (uint32_t) (ntohl(e.address_ipv4.s_addr) << e.bits) != 0)
10153a5a1b3Sopenharmony_ci                pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
10253a5a1b3Sopenharmony_ci
10353a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
10453a5a1b3Sopenharmony_ci        } else if (inet_pton(AF_INET6, a, &e.address_ipv6) > 0) {
10553a5a1b3Sopenharmony_ci
10653a5a1b3Sopenharmony_ci            e.bits = bits == (uint32_t) -1 ? 128 : (int) bits;
10753a5a1b3Sopenharmony_ci
10853a5a1b3Sopenharmony_ci            if (e.bits > 128) {
10953a5a1b3Sopenharmony_ci                pa_log_warn("Number of bits out of range: %i", e.bits);
11053a5a1b3Sopenharmony_ci                goto fail;
11153a5a1b3Sopenharmony_ci            }
11253a5a1b3Sopenharmony_ci            e.family = AF_INET6;
11353a5a1b3Sopenharmony_ci
11453a5a1b3Sopenharmony_ci            if (e.bits < 128) {
11553a5a1b3Sopenharmony_ci                int t = 0, i;
11653a5a1b3Sopenharmony_ci
11753a5a1b3Sopenharmony_ci                for (i = 0, bits = (uint32_t) e.bits; i < 16; i++) {
11853a5a1b3Sopenharmony_ci
11953a5a1b3Sopenharmony_ci                    if (bits >= 8)
12053a5a1b3Sopenharmony_ci                        bits -= 8;
12153a5a1b3Sopenharmony_ci                    else {
12253a5a1b3Sopenharmony_ci                        if ((uint8_t) ((e.address_ipv6.s6_addr[i]) << bits) != 0) {
12353a5a1b3Sopenharmony_ci                            t = 1;
12453a5a1b3Sopenharmony_ci                            break;
12553a5a1b3Sopenharmony_ci                        }
12653a5a1b3Sopenharmony_ci                        bits = 0;
12753a5a1b3Sopenharmony_ci                    }
12853a5a1b3Sopenharmony_ci                }
12953a5a1b3Sopenharmony_ci
13053a5a1b3Sopenharmony_ci                if (t)
13153a5a1b3Sopenharmony_ci                    pa_log_warn("Host part of ACL entry '%s/%u' is not zero!", a, e.bits);
13253a5a1b3Sopenharmony_ci            }
13353a5a1b3Sopenharmony_ci#endif
13453a5a1b3Sopenharmony_ci
13553a5a1b3Sopenharmony_ci        } else {
13653a5a1b3Sopenharmony_ci            pa_log_warn("Failed to parse address: %s", a);
13753a5a1b3Sopenharmony_ci            goto fail;
13853a5a1b3Sopenharmony_ci        }
13953a5a1b3Sopenharmony_ci
14053a5a1b3Sopenharmony_ci        n = pa_xmemdup(&e, sizeof(struct acl_entry));
14153a5a1b3Sopenharmony_ci        PA_LLIST_PREPEND(struct acl_entry, acl->entries, n);
14253a5a1b3Sopenharmony_ci
14353a5a1b3Sopenharmony_ci        pa_xfree(a);
14453a5a1b3Sopenharmony_ci    }
14553a5a1b3Sopenharmony_ci
14653a5a1b3Sopenharmony_ci    return acl;
14753a5a1b3Sopenharmony_ci
14853a5a1b3Sopenharmony_cifail:
14953a5a1b3Sopenharmony_ci    pa_xfree(a);
15053a5a1b3Sopenharmony_ci    pa_ip_acl_free(acl);
15153a5a1b3Sopenharmony_ci
15253a5a1b3Sopenharmony_ci    return NULL;
15353a5a1b3Sopenharmony_ci}
15453a5a1b3Sopenharmony_ci
15553a5a1b3Sopenharmony_civoid pa_ip_acl_free(pa_ip_acl *acl) {
15653a5a1b3Sopenharmony_ci    pa_assert(acl);
15753a5a1b3Sopenharmony_ci
15853a5a1b3Sopenharmony_ci    while (acl->entries) {
15953a5a1b3Sopenharmony_ci        struct acl_entry *e = acl->entries;
16053a5a1b3Sopenharmony_ci        PA_LLIST_REMOVE(struct acl_entry, acl->entries, e);
16153a5a1b3Sopenharmony_ci        pa_xfree(e);
16253a5a1b3Sopenharmony_ci    }
16353a5a1b3Sopenharmony_ci
16453a5a1b3Sopenharmony_ci    pa_xfree(acl);
16553a5a1b3Sopenharmony_ci}
16653a5a1b3Sopenharmony_ci
16753a5a1b3Sopenharmony_ciint pa_ip_acl_check(pa_ip_acl *acl, int fd) {
16853a5a1b3Sopenharmony_ci    struct sockaddr_storage sa;
16953a5a1b3Sopenharmony_ci    struct acl_entry *e;
17053a5a1b3Sopenharmony_ci    socklen_t salen;
17153a5a1b3Sopenharmony_ci
17253a5a1b3Sopenharmony_ci    pa_assert(acl);
17353a5a1b3Sopenharmony_ci    pa_assert(fd >= 0);
17453a5a1b3Sopenharmony_ci
17553a5a1b3Sopenharmony_ci    salen = sizeof(sa);
17653a5a1b3Sopenharmony_ci    if (getpeername(fd, (struct sockaddr*) &sa, &salen) < 0)
17753a5a1b3Sopenharmony_ci        return -1;
17853a5a1b3Sopenharmony_ci
17953a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
18053a5a1b3Sopenharmony_ci    if (sa.ss_family != AF_INET && sa.ss_family != AF_INET6)
18153a5a1b3Sopenharmony_ci#else
18253a5a1b3Sopenharmony_ci    if (sa.ss_family != AF_INET)
18353a5a1b3Sopenharmony_ci#endif
18453a5a1b3Sopenharmony_ci        return -1;
18553a5a1b3Sopenharmony_ci
18653a5a1b3Sopenharmony_ci    if (sa.ss_family == AF_INET && salen != sizeof(struct sockaddr_in))
18753a5a1b3Sopenharmony_ci        return -1;
18853a5a1b3Sopenharmony_ci
18953a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
19053a5a1b3Sopenharmony_ci    if (sa.ss_family == AF_INET6 && salen != sizeof(struct sockaddr_in6))
19153a5a1b3Sopenharmony_ci        return -1;
19253a5a1b3Sopenharmony_ci#endif
19353a5a1b3Sopenharmony_ci
19453a5a1b3Sopenharmony_ci    for (e = acl->entries; e; e = e->next) {
19553a5a1b3Sopenharmony_ci
19653a5a1b3Sopenharmony_ci        if (e->family != sa.ss_family)
19753a5a1b3Sopenharmony_ci            continue;
19853a5a1b3Sopenharmony_ci
19953a5a1b3Sopenharmony_ci        if (e->family == AF_INET) {
20053a5a1b3Sopenharmony_ci            struct sockaddr_in *sai = (struct sockaddr_in*) &sa;
20153a5a1b3Sopenharmony_ci
20253a5a1b3Sopenharmony_ci            if (e->bits == 0 || /* this needs special handling because >> takes the right-hand side modulo 32 */
20353a5a1b3Sopenharmony_ci                (ntohl(sai->sin_addr.s_addr ^ e->address_ipv4.s_addr) >> (32 - e->bits)) == 0)
20453a5a1b3Sopenharmony_ci                return 1;
20553a5a1b3Sopenharmony_ci#ifdef HAVE_IPV6
20653a5a1b3Sopenharmony_ci        } else if (e->family == AF_INET6) {
20753a5a1b3Sopenharmony_ci            int i, bits;
20853a5a1b3Sopenharmony_ci            struct sockaddr_in6 *sai = (struct sockaddr_in6*) &sa;
20953a5a1b3Sopenharmony_ci
21053a5a1b3Sopenharmony_ci            if (e->bits == 128)
21153a5a1b3Sopenharmony_ci                return memcmp(&sai->sin6_addr, &e->address_ipv6, 16) == 0;
21253a5a1b3Sopenharmony_ci
21353a5a1b3Sopenharmony_ci            if (e->bits == 0)
21453a5a1b3Sopenharmony_ci                return 1;
21553a5a1b3Sopenharmony_ci
21653a5a1b3Sopenharmony_ci            for (i = 0, bits = e->bits; i < 16; i++) {
21753a5a1b3Sopenharmony_ci
21853a5a1b3Sopenharmony_ci                if (bits >= 8) {
21953a5a1b3Sopenharmony_ci                    if (sai->sin6_addr.s6_addr[i] != e->address_ipv6.s6_addr[i])
22053a5a1b3Sopenharmony_ci                        break;
22153a5a1b3Sopenharmony_ci
22253a5a1b3Sopenharmony_ci                    bits -= 8;
22353a5a1b3Sopenharmony_ci                } else {
22453a5a1b3Sopenharmony_ci                    if ((sai->sin6_addr.s6_addr[i] ^ e->address_ipv6.s6_addr[i]) >> (8 - bits) != 0)
22553a5a1b3Sopenharmony_ci                        break;
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_ci                    bits = 0;
22853a5a1b3Sopenharmony_ci                }
22953a5a1b3Sopenharmony_ci
23053a5a1b3Sopenharmony_ci                if (bits == 0)
23153a5a1b3Sopenharmony_ci                    return 1;
23253a5a1b3Sopenharmony_ci            }
23353a5a1b3Sopenharmony_ci#endif
23453a5a1b3Sopenharmony_ci        }
23553a5a1b3Sopenharmony_ci    }
23653a5a1b3Sopenharmony_ci
23753a5a1b3Sopenharmony_ci    return 0;
23853a5a1b3Sopenharmony_ci}
239