diff options
| -rw-r--r-- | src/Makefile.am | 14 | ||||
| -rw-r--r-- | src/pulsecore/ipacl.c | 216 | ||||
| -rw-r--r-- | src/pulsecore/ipacl.h | 31 | ||||
| -rw-r--r-- | src/tests/ipacl-test.c | 117 | 
4 files changed, 377 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index d4fbe528..431bedbd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -194,7 +194,8 @@ noinst_PROGRAMS = \  		channelmap-test \  		thread-mainloop-test \  		utf8-test \ -		get-binary-name-test +		get-binary-name-test \ +		ipacl-test  if HAVE_SIGXCPU  noinst_PROGRAMS += \ @@ -227,6 +228,11 @@ get_binary_name_test_CFLAGS = $(AM_CFLAGS)  get_binary_name_test_LDADD = $(AM_LDADD) libpulse.la  get_binary_name_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) +ipacl_test_SOURCES = tests/ipacl-test.c pulsecore/ipacl.c pulsecore/ipacl.h +ipacl_test_CFLAGS = $(AM_CFLAGS) +ipacl_test_LDADD = $(AM_LDADD) libpulsecore.la +ipacl_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) +  mcalign_test_SOURCES = tests/mcalign-test.c  mcalign_test_CFLAGS = $(AM_CFLAGS)  mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la @@ -586,6 +592,7 @@ pulsecoreinclude_HEADERS += \  		pulsecore/socket-util.h \  		pulsecore/iochannel.h \  		pulsecore/socket-server.h \ +		pulsecore/ipacl.h \  		pulsecore/socket-client.h \  		pulsecore/parseaddr.h \  		pulsecore/packet.h \ @@ -615,6 +622,7 @@ modlibexec_LTLIBRARIES = \  		libsocket-util.la \  		libiochannel.la \  		libsocket-server.la \ +		libipacl.la \  		libparseaddr.la \  		libsocket-client.la \  		libpacket.la \ @@ -668,6 +676,10 @@ libsocket_server_la_SOURCES = \  libsocket_server_la_LDFLAGS = -avoid-version  libsocket_server_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-util.la $(LIBWRAP_LIBS) $(WINSOCK_LIBS) +libipacl_la_SOURCES = pulsecore/ipacl.h pulsecore/ipacl.c +libipacl_la_LDFLAGS = -avoid-version +libipacl_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(WINSOCK_LIBS) +  libsocket_client_la_SOURCES = pulsecore/socket-client.c pulsecore/socket-client.h  libsocket_client_la_LDFLAGS = -avoid-version  libsocket_client_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-util.la libparseaddr.la $(LIBASYNCNS_LIBS) $(WINSOCK_LIBS) diff --git a/src/pulsecore/ipacl.c b/src/pulsecore/ipacl.c new file mode 100644 index 00000000..06be0a28 --- /dev/null +++ b/src/pulsecore/ipacl.c @@ -0,0 +1,216 @@ +/* $Id$ */ + +/*** +  This file is part of PulseAudio. +  +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as +  published by the Free Software Foundation; either version 2.1 of the +  License, or (at your option) any later version. +  +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  Lesser General Public License for more details. +  +  You should have received a copy of the GNU Lesser General Public +  License along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <sys/types.h> +#include <arpa/inet.h> +#include <string.h> + +#include <pulsecore/core-util.h> +#include <pulsecore/llist.h> +#include <pulsecore/log.h> +#include <pulse/xmalloc.h> + +#include "ipacl.h" + +struct acl_entry { +    PA_LLIST_FIELDS(struct acl_entry); +    int family;     +    struct in_addr address_ipv4; +    struct in6_addr address_ipv6; +    int bits; +}; + +struct pa_ip_acl { +    PA_LLIST_HEAD(struct acl_entry, entries); +}; + +pa_ip_acl* pa_ip_acl_new(const char *s) { +    const char *state = NULL; +    char *a; +    pa_ip_acl *acl; + +    assert(s); +     +    acl = pa_xnew(pa_ip_acl, 1); +    PA_LLIST_HEAD_INIT(struct acl_entry, acl->entries); +     +    while ((a = pa_split(s, ";", &state))) { +        char *slash; +        struct acl_entry e, *n; +        uint32_t bits; + +        if ((slash = strchr(a, '/'))) { +            *slash = 0; +            slash++; +            if (pa_atou(slash, &bits) < 0) { +                pa_log(__FILE__": failed to parse number of bits: %s", slash); +                goto fail; +            } +        } else +            bits = (uint32_t) -1; + +        if (inet_pton(AF_INET, a, &e.address_ipv4) > 0) { + +            e.bits = bits == (uint32_t) -1 ? 32 : (int) bits; +             +            if (e.bits > 32) { +                pa_log(__FILE__": number of bits out of range: %i", e.bits); +                goto fail; +            } + +            e.family = AF_INET; + +            if (e.bits < 32 && (uint32_t) (ntohl(e.address_ipv4.s_addr) << e.bits) != 0) +                pa_log_warn(__FILE__": WARNING: Host part of ACL entry '%s/%u' is not zero!", a, e.bits); +                 +        } else if (inet_pton(AF_INET6, a, &e.address_ipv6) > 0) { + +            e.bits = bits == (uint32_t) -1 ? 128 : (int) bits; + +            if (e.bits > 128) { +                pa_log(__FILE__": number of bits out of range: %i", e.bits); +                goto fail; +            } +            e.family = AF_INET6; + +            if (e.bits < 128) { +                int t = 0, i; + +                for (i = 0, bits = e.bits; i < 16; i++) { + +                    if (bits >= 8)  +                        bits -= 8; +                    else { +                        if ((uint8_t) ((e.address_ipv6.s6_addr[i]) << bits) != 0) { +                            t = 1; +                            break; +                        } +                        bits = 0; +                    } +                } + +                if (t) +                    pa_log_warn(__FILE__": WARNING: Host part of ACL entry '%s/%u' is not zero!", a, e.bits); +            } +             +        } else { +            pa_log(__FILE__": failed to parse address: %s", a); +            goto fail; +        } + +        n = pa_xmemdup(&e, sizeof(struct acl_entry)); +        PA_LLIST_PREPEND(struct acl_entry, acl->entries, n); +         +        pa_xfree(a); +    } + +    return acl; +     +fail: +    pa_xfree(a); +    pa_ip_acl_free(acl); +     +    return NULL; +} + +void pa_ip_acl_free(pa_ip_acl *acl) { +    assert(acl); + +    while (acl->entries) { +        struct acl_entry *e = acl->entries; +        PA_LLIST_REMOVE(struct acl_entry, acl->entries, e); +        pa_xfree(e); +    } +     +    pa_xfree(acl); +} + +int pa_ip_acl_check(pa_ip_acl *acl, int fd) { +    struct sockaddr_storage sa; +    struct acl_entry *e; +    socklen_t  salen; +         +    assert(acl); +    assert(fd >= 0); + +    salen = sizeof(sa); +    if (getpeername(fd, (struct sockaddr*) &sa, &salen) < 0) +        return -1; + +    if (sa.ss_family != AF_INET && sa.ss_family != AF_INET6) +        return -1; + +    if (sa.ss_family == AF_INET && salen != sizeof(struct sockaddr_in)) +        return -1; + +    if (sa.ss_family == AF_INET6 && salen != sizeof(struct sockaddr_in6)) +        return -1; +     +    for (e = acl->entries; e; e = e->next) { + +        if (e->family != sa.ss_family) +            continue; + +        if (e->family == AF_INET) { +            struct sockaddr_in *sai = (struct sockaddr_in*) &sa; +             +            if (e->bits == 0 || /* this needs special handling because >> takes the right-hand side modulo 32 */ +                (ntohl(sai->sin_addr.s_addr ^ e->address_ipv4.s_addr) >> (32 - e->bits)) == 0) +                return 1; +        } else if (e->family == AF_INET6) { +            int i, bits ; +            struct sockaddr_in6 *sai = (struct sockaddr_in6*) &sa; + +            if (e->bits == 128) +                return memcmp(&sai->sin6_addr, &e->address_ipv6, 16) == 0; + +            if (e->bits == 0) +                return 1; +                 +            for (i = 0, bits = e->bits; i < 16; i++) { + +                if (bits >= 8) { +                    if (sai->sin6_addr.s6_addr[i] != e->address_ipv6.s6_addr[i]) +                        break; + +                    bits -= 8; +                } else { +                    if ((sai->sin6_addr.s6_addr[i] ^ e->address_ipv6.s6_addr[i]) >> (8 - bits) != 0) +                        break; + +                    bits = 0; +                } + +                if (bits == 0) +                    return 1; +            } +        } +    } + +    return 0; +} diff --git a/src/pulsecore/ipacl.h b/src/pulsecore/ipacl.h new file mode 100644 index 00000000..7a4540ce --- /dev/null +++ b/src/pulsecore/ipacl.h @@ -0,0 +1,31 @@ +#ifndef fooparseaddrhfoo +#define fooparseaddrhfoo + +/* $Id$ */ + +/*** +  This file is part of PulseAudio. +  +  PulseAudio is free software; you can redistribute it and/or modify +  it under the terms of the GNU Lesser General Public License as +  published by the Free Software Foundation; either version 2.1 of the +  License, or (at your option) any later version. +  +  PulseAudio is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +  Lesser General Public License for more details. +  +  You should have received a copy of the GNU Lesser General Public +  License along with PulseAudio; if not, write to the Free Software +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +  USA. +***/ + +typedef struct pa_ip_acl pa_ip_acl; + +pa_ip_acl* pa_ip_acl_new(const char *s); +void pa_ip_acl_free(pa_ip_acl *acl); +int pa_ip_acl_check(pa_ip_acl *acl, int fd); + +#endif diff --git a/src/tests/ipacl-test.c b/src/tests/ipacl-test.c new file mode 100644 index 00000000..b98151ee --- /dev/null +++ b/src/tests/ipacl-test.c @@ -0,0 +1,117 @@ +/* $Id$ */ + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <stdio.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <assert.h> +#include <string.h> + +#include <pulsecore/ipacl.h> + +int main(int argc, char *argv[]) { +    struct sockaddr_in sa; +    struct sockaddr_in6 sa6; +    int fd; +    int r; +    pa_ip_acl *acl; + +    fd = socket(PF_INET, SOCK_STREAM, 0);  +    assert(fd >= 0); +     +    sa.sin_family = AF_INET; +    sa.sin_port = htons(22); +    sa.sin_addr.s_addr = inet_addr("127.0.0.1"); +         +    r = connect(fd, (struct sockaddr*) &sa, sizeof(sa)); +    assert(r >= 0); + +    acl = pa_ip_acl_new("127.0.0.1"); +    assert(acl); +    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); + +    acl = pa_ip_acl_new("127.0.0.2/0"); +    assert(acl); +    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); + +    acl = pa_ip_acl_new("127.0.0.1/32"); +    assert(acl); +    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); + +    acl = pa_ip_acl_new("127.0.0.1/7"); +    assert(acl); +    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); +     +    acl = pa_ip_acl_new("127.0.0.2"); +    assert(acl); +    printf("result=%u (should be 0)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); + +    acl = pa_ip_acl_new("127.0.0.0/8;0.0.0.0/32"); +    assert(acl); +    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); + +    acl = pa_ip_acl_new("128.0.0.2/9"); +    assert(acl); +    printf("result=%u (should be 0)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); + +    acl = pa_ip_acl_new("::1/9"); +    assert(acl); +    printf("result=%u (should be 0)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); +     +    close(fd); + +    fd = socket(PF_INET6, SOCK_STREAM, 0); +    assert(fd >= 0); + +    memset(&sa6, 0, sizeof(sa6)); +    sa6.sin6_family = AF_INET6; +    sa6.sin6_port = htons(22); +    inet_pton(AF_INET6, "::1", &sa6.sin6_addr); +         +    r = connect(fd, (struct sockaddr*) &sa6, sizeof(sa6)); +    assert(r >= 0); + +    acl = pa_ip_acl_new("::1"); +    assert(acl); +    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); + +    acl = pa_ip_acl_new("::1/9"); +    assert(acl); +    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); + +    acl = pa_ip_acl_new("::/0"); +    assert(acl); +    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); + +    acl = pa_ip_acl_new("::2/128"); +    assert(acl); +    printf("result=%u (should be 0)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); + +    acl = pa_ip_acl_new("::2/127"); +    assert(acl); +    printf("result=%u (should be 0)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); + +    acl = pa_ip_acl_new("::2/126"); +    assert(acl); +    printf("result=%u (should be 1)\n", pa_ip_acl_check(acl, fd)); +    pa_ip_acl_free(acl); + +    close(fd); +     +    return 0; +}  | 
