diff options
Diffstat (limited to 'lib/libcasper/services/cap_net/tests/net_test.c')
-rw-r--r-- | lib/libcasper/services/cap_net/tests/net_test.c | 1487 |
1 files changed, 1487 insertions, 0 deletions
diff --git a/lib/libcasper/services/cap_net/tests/net_test.c b/lib/libcasper/services/cap_net/tests/net_test.c new file mode 100644 index 000000000000..adf5773233c8 --- /dev/null +++ b/lib/libcasper/services/cap_net/tests/net_test.c @@ -0,0 +1,1487 @@ +/*- + * Copyright (c) 2020 Mariusz Zaborski <oshogbo@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <netdb.h> + +#include <atf-c.h> + +#include <libcasper.h> +#include <casper/cap_net.h> + +#define TEST_DOMAIN_0 "example.com" +#define TEST_DOMAIN_1 "freebsd.org" +#define TEST_IPV4 "1.1.1.1" +#define TEST_IPV6 "2001:4860:4860::8888" +#define TEST_BIND_IPV4 "127.0.0.1" +#define TEST_PORT 80 +#define TEST_PORT_STR "80" + +static cap_channel_t * +create_network_service(void) +{ + cap_channel_t *capcas, *capnet; + + capcas = cap_init(); + ATF_REQUIRE(capcas != NULL); + + capnet = cap_service_open(capcas, "system.net"); + ATF_REQUIRE(capnet != NULL); + + cap_close(capcas); + return (capnet); +} + +static int +test_getnameinfo_v4(cap_channel_t *chan, int family, const char *ip) +{ + struct sockaddr_in ipaddr; + char capfn[MAXHOSTNAMELEN]; + char origfn[MAXHOSTNAMELEN]; + int capret, sysret; + + memset(&ipaddr, 0, sizeof(ipaddr)); + ipaddr.sin_family = family; + inet_pton(family, ip, &ipaddr.sin_addr); + + capret = cap_getnameinfo(chan, (struct sockaddr *)&ipaddr, sizeof(ipaddr), + capfn, sizeof(capfn), NULL, 0, NI_NAMEREQD); + if (capret != 0 && capret == ENOTCAPABLE) + return (ENOTCAPABLE); + + sysret = getnameinfo((struct sockaddr *)&ipaddr, sizeof(ipaddr), origfn, + sizeof(origfn), NULL, 0, NI_NAMEREQD); + if (sysret != 0) { + atf_tc_skip("getnameinfo(%s) failed: %s", + ip, gai_strerror(sysret)); + } + ATF_REQUIRE(capret == 0); + ATF_REQUIRE(strcmp(origfn, capfn) == 0); + + return (0); +} + +static int +test_getnameinfo_v6(cap_channel_t *chan, const char *ip) +{ + struct sockaddr_in6 ipaddr; + char capfn[MAXHOSTNAMELEN]; + char origfn[MAXHOSTNAMELEN]; + int capret, sysret; + + memset(&ipaddr, 0, sizeof(ipaddr)); + ipaddr.sin6_family = AF_INET6; + inet_pton(AF_INET6, ip, &ipaddr.sin6_addr); + + capret = cap_getnameinfo(chan, (struct sockaddr *)&ipaddr, sizeof(ipaddr), + capfn, sizeof(capfn), NULL, 0, NI_NAMEREQD); + if (capret != 0 && capret == ENOTCAPABLE) + return (ENOTCAPABLE); + + sysret = getnameinfo((struct sockaddr *)&ipaddr, sizeof(ipaddr), origfn, + sizeof(origfn), NULL, 0, NI_NAMEREQD); + if (sysret != 0) { + atf_tc_skip("getnameinfo(%s) failed: %s", + ip, gai_strerror(sysret)); + } + ATF_REQUIRE(capret == 0); + ATF_REQUIRE(strcmp(origfn, capfn) == 0); + + return (0); +} + +static int +test_getnameinfo(cap_channel_t *chan, int family, const char *ip) +{ + + if (family == AF_INET6) { + return (test_getnameinfo_v6(chan, ip)); + } + + return (test_getnameinfo_v4(chan, family, ip)); +} + +static int +test_gethostbyaddr_v4(cap_channel_t *chan, int family, const char *ip) +{ + struct in_addr ipaddr; + struct hostent *caphp, *orighp; + + memset(&ipaddr, 0, sizeof(ipaddr)); + inet_pton(AF_INET, ip, &ipaddr); + + caphp = cap_gethostbyaddr(chan, &ipaddr, sizeof(ipaddr), family); + if (caphp == NULL && h_errno == ENOTCAPABLE) + return (ENOTCAPABLE); + + orighp = gethostbyaddr(&ipaddr, sizeof(ipaddr), family); + if (orighp == NULL) + atf_tc_skip("gethostbyaddr(%s) failed", ip); + ATF_REQUIRE(caphp != NULL); + ATF_REQUIRE(strcmp(orighp->h_name, caphp->h_name) == 0); + + return (0); +} + +static int +test_gethostbyaddr_v6(cap_channel_t *chan, const char *ip) +{ + struct in6_addr ipaddr; + struct hostent *caphp, *orighp; + + memset(&ipaddr, 0, sizeof(ipaddr)); + inet_pton(AF_INET6, ip, &ipaddr); + + caphp = cap_gethostbyaddr(chan, &ipaddr, sizeof(ipaddr), AF_INET6); + if (caphp == NULL && h_errno == ENOTCAPABLE) + return (ENOTCAPABLE); + + orighp = gethostbyaddr(&ipaddr, sizeof(ipaddr), AF_INET6); + if (orighp == NULL) + atf_tc_skip("gethostbyaddr(%s) failed", ip); + ATF_REQUIRE(caphp != NULL); + ATF_REQUIRE(strcmp(orighp->h_name, caphp->h_name) == 0); + + return (0); +} + +static int +test_gethostbyaddr(cap_channel_t *chan, int family, const char *ip) +{ + + if (family == AF_INET6) { + return (test_gethostbyaddr_v6(chan, ip)); + } else { + return (test_gethostbyaddr_v4(chan, family, ip)); + } +} + +static int +test_getaddrinfo(cap_channel_t *chan, int family, const char *domain, + const char *servname) +{ + struct addrinfo hints, *capres, *origres, *res0, *res1; + bool found; + int capret, sysret; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + + capret = cap_getaddrinfo(chan, domain, servname, &hints, &capres); + if (capret != 0 && capret == ENOTCAPABLE) + return (capret); + + sysret = getaddrinfo(domain, servname, &hints, &origres); + if (sysret != 0) + atf_tc_skip("getaddrinfo(%s) failed: %s", + domain, gai_strerror(sysret)); + ATF_REQUIRE(capret == 0); + + for (res0 = capres; res0 != NULL; res0 = res0->ai_next) { + found = false; + for (res1 = origres; res1 != NULL; res1 = res1->ai_next) { + if (res1->ai_addrlen == res0->ai_addrlen && + memcmp(res1->ai_addr, res0->ai_addr, + res0->ai_addrlen) == 0) { + found = true; + break; + } + } + ATF_REQUIRE(found); + } + + freeaddrinfo(capres); + freeaddrinfo(origres); + return (0); +} + +static int +test_gethostbyname(cap_channel_t *chan, int family, const char *domain) +{ + struct hostent *caphp, *orighp; + + caphp = cap_gethostbyname2(chan, domain, family); + if (caphp == NULL && h_errno == ENOTCAPABLE) + return (h_errno); + + orighp = gethostbyname2(domain, family); + if (orighp == NULL) + atf_tc_skip("gethostbyname2(%s) failed", domain); + + ATF_REQUIRE(caphp != NULL); + ATF_REQUIRE(strcmp(caphp->h_name, orighp->h_name) == 0); + return (0); +} + +static int +test_bind(cap_channel_t *chan, const char *ip) +{ + struct sockaddr_in ipv4; + int capfd, ret, serrno; + + capfd = socket(AF_INET, SOCK_STREAM, 0); + ATF_REQUIRE(capfd > 0); + + memset(&ipv4, 0, sizeof(ipv4)); + ipv4.sin_family = AF_INET; + inet_pton(AF_INET, ip, &ipv4.sin_addr); + + ret = cap_bind(chan, capfd, (struct sockaddr *)&ipv4, sizeof(ipv4)); + serrno = errno; + close(capfd); + + return (ret < 0 ? serrno : 0); +} + +static int +test_connect(cap_channel_t *chan, const char *ip, unsigned short port) +{ + struct sockaddr_in ipv4; + int capfd, ret, serrno; + + capfd = socket(AF_INET, SOCK_STREAM, 0); + ATF_REQUIRE(capfd >= 0); + + memset(&ipv4, 0, sizeof(ipv4)); + ipv4.sin_family = AF_INET; + ipv4.sin_port = htons(port); + inet_pton(AF_INET, ip, &ipv4.sin_addr); + + ret = cap_connect(chan, capfd, (struct sockaddr *)&ipv4, sizeof(ipv4)); + serrno = errno; + ATF_REQUIRE(close(capfd) == 0); + + if (ret < 0 && serrno != ENOTCAPABLE) { + int sd; + + /* + * If the connection failed, it might be because we can't reach + * the destination host. To check, try a plain connect() and + * see if it fails with the same error. + */ + sd = socket(AF_INET, SOCK_STREAM, 0); + ATF_REQUIRE(sd >= 0); + + memset(&ipv4, 0, sizeof(ipv4)); + ipv4.sin_family = AF_INET; + ipv4.sin_port = htons(port); + inet_pton(AF_INET, ip, &ipv4.sin_addr); + ret = connect(sd, (struct sockaddr *)&ipv4, sizeof(ipv4)); + ATF_REQUIRE(ret < 0); + ATF_REQUIRE_MSG(errno == serrno, "errno %d != serrno %d", + errno, serrno); + ATF_REQUIRE(close(sd) == 0); + atf_tc_skip("connect(%s:%d) failed: %s", + ip, port, strerror(serrno)); + } + + return (ret < 0 ? serrno : 0); +} + +static void +test_extend_mode(cap_channel_t *capnet, int current) +{ + cap_net_limit_t *limit; + const int rights[] = { + CAPNET_ADDR2NAME, + CAPNET_NAME2ADDR, + CAPNET_DEPRECATED_ADDR2NAME, + CAPNET_DEPRECATED_NAME2ADDR, + CAPNET_CONNECT, + CAPNET_BIND, + CAPNET_CONNECTDNS + }; + size_t i; + + for (i = 0; i < nitems(rights); i++) { + if (current == rights[i]) + continue; + + limit = cap_net_limit_init(capnet, current | rights[i]); + ATF_REQUIRE(limit != NULL); + ATF_REQUIRE(cap_net_limit(limit) != 0); + } +} + +ATF_TC(capnet__getnameinfo); +ATF_TC_HEAD(capnet__getnameinfo, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__getnameinfo, tc) +{ + cap_channel_t *capnet; + + capnet = create_network_service(); + + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == 0); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET6, TEST_IPV6) == 0); + + cap_close(capnet); +} + +ATF_TC(capnet__connect); +ATF_TC_HEAD(capnet__connect, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__connect, tc) +{ + cap_channel_t *capnet; + + capnet = create_network_service(); + + ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == 0); + + cap_close(capnet); +} + +ATF_TC(capnet__bind); +ATF_TC_HEAD(capnet__bind, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__bind, tc) +{ + cap_channel_t *capnet; + + capnet = create_network_service(); + + ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == 0); + + cap_close(capnet); +} + +ATF_TC(capnet__getaddrinfo); +ATF_TC_HEAD(capnet__getaddrinfo, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__getaddrinfo, tc) +{ + cap_channel_t *capnet; + struct addrinfo hints, *capres; + + capnet = create_network_service(); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + ATF_REQUIRE(cap_getaddrinfo(capnet, TEST_IPV4, "80", &hints, &capres) == + 0); + + cap_close(capnet); +} + +ATF_TC(capnet__gethostbyname); +ATF_TC_HEAD(capnet__gethostbyname, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__gethostbyname, tc) +{ + cap_channel_t *capnet; + + capnet = create_network_service(); + + ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == 0); + + cap_close(capnet); +} + +ATF_TC(capnet__gethostbyaddr); +ATF_TC_HEAD(capnet__gethostbyaddr, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__gethostbyaddr, tc) +{ + cap_channel_t *capnet; + + capnet = create_network_service(); + + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == 0); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET6, TEST_IPV6) == 0); + + cap_close(capnet); +} + +ATF_TC(capnet__getnameinfo_buffer); +ATF_TC_HEAD(capnet__getnameinfo_buffer, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__getnameinfo_buffer, tc) +{ + cap_channel_t *chan; + struct sockaddr_in sin; + int ret; + struct { + char host[sizeof(TEST_IPV4)]; + char host_canary; + char serv[sizeof(TEST_PORT_STR)]; + char serv_canary; + } buffers; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(TEST_PORT); + ret = inet_pton(AF_INET, TEST_IPV4, &sin.sin_addr); + ATF_REQUIRE_EQ(1, ret); + + memset(&buffers, '!', sizeof(buffers)); + + chan = create_network_service(); + ret = cap_getnameinfo(chan, (struct sockaddr *)&sin, sizeof(sin), + buffers.host, sizeof(buffers.host), + buffers.serv, sizeof(buffers.serv), + NI_NUMERICHOST | NI_NUMERICSERV); + ATF_REQUIRE_EQ_MSG(0, ret, "%d", ret); + + // Verify that cap_getnameinfo worked with minimally sized buffers. + ATF_CHECK_EQ(0, strcmp(TEST_IPV4, buffers.host)); + ATF_CHECK_EQ(0, strcmp(TEST_PORT_STR, buffers.serv)); + + // Verify that cap_getnameinfo did not overflow the buffers. + ATF_CHECK_EQ('!', buffers.host_canary); + ATF_CHECK_EQ('!', buffers.serv_canary); + + cap_close(chan); +} + +ATF_TC(capnet__limits_addr2name_mode); +ATF_TC_HEAD(capnet__limits_addr2name_mode, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_addr2name_mode, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + + capnet = create_network_service(); + + /* LIMIT */ + limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME); + ATF_REQUIRE(limit != NULL); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + /* ALLOWED */ + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == 0); + + /* DISALLOWED */ + ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == + ENOTCAPABLE); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == ENOTCAPABLE); + ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == ENOTCAPABLE); + + test_extend_mode(capnet, CAPNET_ADDR2NAME); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_addr2name_family); +ATF_TC_HEAD(capnet__limits_addr2name_family, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_addr2name_family, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + int family[] = { AF_INET6, AF_INET }; + + capnet = create_network_service(); + + /* Limit to AF_INET6 and AF_INET. */ + limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME); + ATF_REQUIRE(limit != NULL); + cap_net_limit_addr2name_family(limit, family, nitems(family)); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == 0); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET6, TEST_IPV6) == 0); + + /* Limit to AF_INET6 and AF_INET. */ + limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME); + ATF_REQUIRE(limit != NULL); + cap_net_limit_addr2name_family(limit, &family[0], 1); + cap_net_limit_addr2name_family(limit, &family[1], 1); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == 0); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET6, TEST_IPV6) == 0); + + /* Limit to AF_INET6. */ + limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME); + ATF_REQUIRE(limit != NULL); + cap_net_limit_addr2name_family(limit, family, 1); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET6, TEST_IPV6) == 0); + + /* Unable to set empty limits. Empty limits means full access. */ + limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_addr2name); +ATF_TC_HEAD(capnet__limits_addr2name, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_addr2name, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + struct sockaddr_in ipaddrv4; + struct sockaddr_in6 ipaddrv6; + + capnet = create_network_service(); + + /* Limit to TEST_IPV4 and TEST_IPV6. */ + memset(&ipaddrv4, 0, sizeof(ipaddrv4)); + memset(&ipaddrv6, 0, sizeof(ipaddrv6)); + + ipaddrv4.sin_family = AF_INET; + inet_pton(AF_INET, TEST_IPV4, &ipaddrv4.sin_addr); + + ipaddrv6.sin6_family = AF_INET6; + inet_pton(AF_INET6, TEST_IPV6, &ipaddrv6.sin6_addr); + + limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME); + ATF_REQUIRE(limit != NULL); + + cap_net_limit_addr2name(limit, (struct sockaddr *)&ipaddrv4, + sizeof(ipaddrv4)); + cap_net_limit_addr2name(limit, (struct sockaddr *)&ipaddrv6, + sizeof(ipaddrv6)); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == 0); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET6, TEST_IPV6) == 0); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, "127.0.0.1") == + ENOTCAPABLE); + + /* Limit to AF_INET. */ + limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME); + ATF_REQUIRE(limit != NULL); + cap_net_limit_addr2name(limit, (struct sockaddr *)&ipaddrv4, + sizeof(ipaddrv4)); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == 0); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET6, TEST_IPV6) == + ENOTCAPABLE); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, "127.0.0.1") == + ENOTCAPABLE); + + /* Unable to set empty limits. Empty limits means full access. */ + limit = cap_net_limit_init(capnet, CAPNET_ADDR2NAME); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_deprecated_addr2name_mode); +ATF_TC_HEAD(capnet__limits_deprecated_addr2name_mode, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_deprecated_addr2name_mode, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + + capnet = create_network_service(); + + /* LIMIT */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME); + ATF_REQUIRE(limit != NULL); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + /* ALLOWED */ + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == 0); + + /* DISALLOWED */ + ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == + ENOTCAPABLE); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == ENOTCAPABLE); + ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == ENOTCAPABLE); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_deprecated_addr2name_family); +ATF_TC_HEAD(capnet__limits_deprecated_addr2name_family, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_deprecated_addr2name_family, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + int family[] = { AF_INET6, AF_INET }; + + capnet = create_network_service(); + + /* Limit to AF_INET6 and AF_INET. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME); + ATF_REQUIRE(limit != NULL); + cap_net_limit_addr2name_family(limit, family, nitems(family)); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == 0); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET6, TEST_IPV6) == 0); + ATF_REQUIRE(test_gethostbyaddr(capnet, PF_LINK, TEST_IPV4) == + ENOTCAPABLE); + + /* Limit to AF_INET6 and AF_INET. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME); + ATF_REQUIRE(limit != NULL); + cap_net_limit_addr2name_family(limit, &family[0], 1); + cap_net_limit_addr2name_family(limit, &family[1], 1); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == 0); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET6, TEST_IPV6) == 0); + ATF_REQUIRE(test_gethostbyaddr(capnet, PF_LINK, TEST_IPV4) == + ENOTCAPABLE); + + /* Limit to AF_INET6. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME); + ATF_REQUIRE(limit != NULL); + cap_net_limit_addr2name_family(limit, family, 1); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET6, TEST_IPV6) == 0); + ATF_REQUIRE(test_gethostbyaddr(capnet, PF_LINK, TEST_IPV4) == + ENOTCAPABLE); + + /* Unable to set empty limits. Empty limits means full access. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_deprecated_addr2name); +ATF_TC_HEAD(capnet__limits_deprecated_addr2name, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_deprecated_addr2name, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + struct in_addr ipaddrv4; + struct in6_addr ipaddrv6; + + capnet = create_network_service(); + + /* Limit to TEST_IPV4 and TEST_IPV6. */ + memset(&ipaddrv4, 0, sizeof(ipaddrv4)); + memset(&ipaddrv6, 0, sizeof(ipaddrv6)); + + inet_pton(AF_INET, TEST_IPV4, &ipaddrv4); + inet_pton(AF_INET6, TEST_IPV6, &ipaddrv6); + + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME); + ATF_REQUIRE(limit != NULL); + + cap_net_limit_addr2name(limit, (struct sockaddr *)&ipaddrv4, + sizeof(ipaddrv4)); + cap_net_limit_addr2name(limit, (struct sockaddr *)&ipaddrv6, + sizeof(ipaddrv6)); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == 0); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET6, TEST_IPV6) == 0); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, "127.0.0.1") == + ENOTCAPABLE); + + /* Limit to AF_INET. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME); + ATF_REQUIRE(limit != NULL); + cap_net_limit_addr2name(limit, (struct sockaddr *)&ipaddrv4, + sizeof(ipaddrv4)); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == 0); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET6, TEST_IPV6) == + ENOTCAPABLE); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, "127.0.0.1") == + ENOTCAPABLE); + + /* Unable to set empty limits. Empty limits means full access. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_ADDR2NAME); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + cap_close(capnet); +} + + +ATF_TC(capnet__limits_name2addr_mode); +ATF_TC_HEAD(capnet__limits_name2addr_mode, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_name2addr_mode, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + + capnet = create_network_service(); + + /* LIMIT */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + /* ALLOWED */ + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + 0); + + /* DISALLOWED */ + ATF_REQUIRE( + test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == ENOTCAPABLE); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == ENOTCAPABLE); + ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == ENOTCAPABLE); + + test_extend_mode(capnet, CAPNET_ADDR2NAME); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_name2addr_hosts); +ATF_TC_HEAD(capnet__limits_name2addr_hosts, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_name2addr_hosts, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + + capnet = create_network_service(); + + /* Limit to TEST_DOMAIN_0 and localhost only. */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL); + cap_net_limit_name2addr(limit, "localhost", NULL); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, "localhost", NULL) == 0); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, NULL) == + ENOTCAPABLE); + + /* Limit to TEST_DOMAIN_0 only. */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, "localhost", NULL) == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, NULL) == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + 0); + + /* Unable to set empty limits. Empty limits means full access. */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + /* Try to extend the limit. */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_1, NULL); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_1, NULL); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_name2addr_hosts_servnames_strict); +ATF_TC_HEAD(capnet__limits_name2addr_hosts_servnames_strict, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_name2addr_hosts_servnames_strict, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + + capnet = create_network_service(); + + /* + * Limit to TEST_DOMAIN_0 and HTTP service. + */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, "http"); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, "http") == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, "snmp") == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, "http") == + ENOTCAPABLE); + + /* Unable to set empty limits. Empty limits means full access. */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_name2addr_hosts_servnames_mix); +ATF_TC_HEAD(capnet__limits_name2addr_hosts_servnames_mix, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_name2addr_hosts_servnames_mix, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + + capnet = create_network_service(); + + /* + * Limit to TEST_DOMAIN_0 and any servnamex, and any domain with + * servname HTTP. + */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL); + cap_net_limit_name2addr(limit, NULL, "http"); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, "http") == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, "http") == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, "snmp") == + ENOTCAPABLE); + + /* Limit to HTTP servname only. */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, NULL, "http"); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, "http") == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, "http") == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_1, "snmp") == + ENOTCAPABLE); + + /* Unable to set empty limits. Empty limits means full access. */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_name2addr_family); +ATF_TC_HEAD(capnet__limits_name2addr_family, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_name2addr_family, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + int family[] = { AF_INET6, AF_INET }; + + capnet = create_network_service(); + + /* Limit to AF_INET and AF_INET6. */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL); + cap_net_limit_name2addr_family(limit, family, nitems(family)); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET6, TEST_DOMAIN_0, NULL) == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, PF_LINK, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + + /* Limit to AF_INET and AF_INET6. */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL); + cap_net_limit_name2addr_family(limit, &family[0], 1); + cap_net_limit_name2addr_family(limit, &family[1], 1); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET6, TEST_DOMAIN_0, NULL) == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, PF_LINK, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + + /* Limit to AF_INET6 only. */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL); + cap_net_limit_name2addr_family(limit, family, 1); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET6, TEST_DOMAIN_0, NULL) == + 0); + ATF_REQUIRE(test_getaddrinfo(capnet, PF_LINK, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + + /* Unable to set empty limits. Empty limits means full access. */ + limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_deprecated_name2addr_mode); +ATF_TC_HEAD(capnet__limits_deprecated_name2addr_mode, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_deprecated_name2addr_mode, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + + capnet = create_network_service(); + + /* LIMIT */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + /* ALLOWED */ + ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == 0); + + /* DISALLOWED */ + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == ENOTCAPABLE); + ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == ENOTCAPABLE); + + test_extend_mode(capnet, CAPNET_ADDR2NAME); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_deprecated_name2addr_hosts); +ATF_TC_HEAD(capnet__limits_deprecated_name2addr_hosts, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_deprecated_name2addr_hosts, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + + capnet = create_network_service(); + + /* Limit to TEST_DOMAIN_0 and localhost only. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL); + cap_net_limit_name2addr(limit, "localhost", NULL); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == 0); + ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, "localhost") == 0); + ATF_REQUIRE( + test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_1) == ENOTCAPABLE); + + /* Limit to TEST_DOMAIN_0 only. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE( + test_gethostbyname(capnet, AF_INET, "localhost") == ENOTCAPABLE); + ATF_REQUIRE( + test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_1) == ENOTCAPABLE); + ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == 0); + + /* Unable to set empty limits. Empty limits means full access. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_deprecated_name2addr_family); +ATF_TC_HEAD(capnet__limits_deprecated_name2addr_family, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_deprecated_name2addr_family, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + int family[] = { AF_INET6, AF_INET }; + + capnet = create_network_service(); + + /* Limit to AF_INET and AF_INET6. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL); + cap_net_limit_name2addr_family(limit, family, nitems(family)); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == 0); + ATF_REQUIRE(test_gethostbyname(capnet, AF_INET6, TEST_DOMAIN_0) == 0); + ATF_REQUIRE( + test_gethostbyname(capnet, PF_LINK, TEST_DOMAIN_0) == ENOTCAPABLE); + + /* Limit to AF_INET and AF_INET6. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL); + cap_net_limit_name2addr_family(limit, &family[0], 1); + cap_net_limit_name2addr_family(limit, &family[1], 1); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == 0); + ATF_REQUIRE(test_gethostbyname(capnet, AF_INET6, TEST_DOMAIN_0) == 0); + ATF_REQUIRE( + test_gethostbyname(capnet, PF_LINK, TEST_DOMAIN_0) == ENOTCAPABLE); + + /* Limit to AF_INET6 only. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_DOMAIN_0, NULL); + cap_net_limit_name2addr_family(limit, family, 1); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE( + test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == ENOTCAPABLE); + ATF_REQUIRE(test_gethostbyname(capnet, AF_INET6, TEST_DOMAIN_0) == 0); + ATF_REQUIRE( + test_gethostbyname(capnet, PF_LINK, TEST_DOMAIN_0) == ENOTCAPABLE); + + /* Unable to set empty limits. Empty limits means full access. */ + limit = cap_net_limit_init(capnet, CAPNET_DEPRECATED_NAME2ADDR); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_bind_mode); +ATF_TC_HEAD(capnet__limits_bind_mode, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_bind_mode, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + + capnet = create_network_service(); + + /* LIMIT */ + limit = cap_net_limit_init(capnet, CAPNET_BIND); + ATF_REQUIRE(limit != NULL); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + /* ALLOWED */ + ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == 0); + + /* DISALLOWED */ + ATF_REQUIRE( + test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == ENOTCAPABLE); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == ENOTCAPABLE); + + test_extend_mode(capnet, CAPNET_ADDR2NAME); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_bind); +ATF_TC_HEAD(capnet__limits_bind, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_bind, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + struct sockaddr_in ipv4; + + capnet = create_network_service(); + + limit = cap_net_limit_init(capnet, CAPNET_BIND); + ATF_REQUIRE(limit != NULL); + + memset(&ipv4, 0, sizeof(ipv4)); + ipv4.sin_family = AF_INET; + inet_pton(AF_INET, TEST_BIND_IPV4, &ipv4.sin_addr); + + cap_net_limit_bind(limit, (struct sockaddr *)&ipv4, sizeof(ipv4)); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == 0); + ATF_REQUIRE(test_bind(capnet, "127.0.0.2") == ENOTCAPABLE); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_connect_mode); +ATF_TC_HEAD(capnet__limits_connect_mode, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_connect_mode, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + + capnet = create_network_service(); + + /* LIMIT */ + limit = cap_net_limit_init(capnet, CAPNET_CONNECT); + ATF_REQUIRE(limit != NULL); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + /* ALLOWED */ + ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == 0); + + /* DISALLOWED */ + ATF_REQUIRE( + test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == ENOTCAPABLE); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == ENOTCAPABLE); + + test_extend_mode(capnet, CAPNET_ADDR2NAME); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_connect_dns_mode); +ATF_TC_HEAD(capnet__limits_connect_dns_mode, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_connect_dns_mode, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + + capnet = create_network_service(); + + /* LIMIT */ + limit = cap_net_limit_init(capnet, CAPNET_CONNECT | CAPNET_CONNECTDNS); + ATF_REQUIRE(limit != NULL); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + /* ALLOWED */ + ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == 0); + + /* DISALLOWED */ + ATF_REQUIRE( + test_gethostbyname(capnet, AF_INET, TEST_DOMAIN_0) == ENOTCAPABLE); + ATF_REQUIRE(test_getnameinfo(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_gethostbyaddr(capnet, AF_INET, TEST_IPV4) == + ENOTCAPABLE); + ATF_REQUIRE(test_getaddrinfo(capnet, AF_INET, TEST_DOMAIN_0, NULL) == + ENOTCAPABLE); + ATF_REQUIRE(test_bind(capnet, TEST_BIND_IPV4) == ENOTCAPABLE); + + test_extend_mode(capnet, CAPNET_ADDR2NAME); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_connect); +ATF_TC_HEAD(capnet__limits_connect, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_connect, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + struct sockaddr_in ipv4; + + capnet = create_network_service(); + + /* Limit only to TEST_IPV4 on port 80 and 443. */ + limit = cap_net_limit_init(capnet, CAPNET_CONNECT); + ATF_REQUIRE(limit != NULL); + memset(&ipv4, 0, sizeof(ipv4)); + ipv4.sin_family = AF_INET; + ipv4.sin_port = htons(80); + inet_pton(AF_INET, TEST_IPV4, &ipv4.sin_addr); + cap_net_limit_connect(limit, (struct sockaddr *)&ipv4, sizeof(ipv4)); + + ipv4.sin_port = htons(443); + cap_net_limit_connect(limit, (struct sockaddr *)&ipv4, sizeof(ipv4)); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 80) == 0); + ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 80) == ENOTCAPABLE); + ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 433) == ENOTCAPABLE); + ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 443) == 0); + + /* Limit only to TEST_IPV4 on port 443. */ + limit = cap_net_limit_init(capnet, CAPNET_CONNECT); + cap_net_limit_connect(limit, (struct sockaddr *)&ipv4, sizeof(ipv4)); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 433) == ENOTCAPABLE); + ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 80) == ENOTCAPABLE); + ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 433) == ENOTCAPABLE); + ATF_REQUIRE(test_connect(capnet, TEST_IPV4, 443) == 0); + + /* Unable to set empty limits. Empty limits means full access. */ + limit = cap_net_limit_init(capnet, CAPNET_CONNECT); + ATF_REQUIRE(cap_net_limit(limit) != 0); + + cap_close(capnet); +} + +ATF_TC(capnet__limits_connecttodns); +ATF_TC_HEAD(capnet__limits_connecttodns, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_connecttodns, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + struct addrinfo hints, *capres, *res; + int family[] = { AF_INET }; + int error; + + capnet = create_network_service(); + + limit = cap_net_limit_init(capnet, CAPNET_CONNECTDNS | + CAPNET_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_IPV4, "80"); + cap_net_limit_name2addr_family(limit, family, 1); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 433) == ENOTCAPABLE); + ATF_REQUIRE(cap_getaddrinfo(capnet, TEST_IPV4, "80", &hints, &capres) == + 0); + ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 433) == ENOTCAPABLE); + + for (res = capres; res != NULL; res = res->ai_next) { + int s; + + ATF_REQUIRE(res->ai_family == AF_INET); + ATF_REQUIRE(res->ai_socktype == SOCK_STREAM); + + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + ATF_REQUIRE(s >= 0); + + error = cap_connect(capnet, s, res->ai_addr, + res->ai_addrlen); + if (error != 0 && errno != ENOTCAPABLE) + atf_tc_skip("unable to connect: %s", strerror(errno)); + ATF_REQUIRE(error == 0); + ATF_REQUIRE(close(s) == 0); + } + + freeaddrinfo(capres); + cap_close(capnet); +} + + +ATF_TC(capnet__limits_deprecated_connecttodns); +ATF_TC_HEAD(capnet__limits_deprecated_connecttodns, tc) +{ + atf_tc_set_md_var(tc, "require.config", "allow_network_access"); +} +ATF_TC_BODY(capnet__limits_deprecated_connecttodns, tc) +{ + cap_channel_t *capnet; + cap_net_limit_t *limit; + struct hostent *caphp; + struct in_addr ipaddr; + struct sockaddr_in connaddr; + int family[] = { AF_INET }; + int error, i; + + capnet = create_network_service(); + + limit = cap_net_limit_init(capnet, CAPNET_CONNECTDNS | + CAPNET_DEPRECATED_NAME2ADDR); + ATF_REQUIRE(limit != NULL); + cap_net_limit_name2addr(limit, TEST_IPV4, NULL); + cap_net_limit_name2addr_family(limit, family, 1); + ATF_REQUIRE(cap_net_limit(limit) == 0); + + memset(&ipaddr, 0, sizeof(ipaddr)); + inet_pton(AF_INET, TEST_IPV4, &ipaddr); + + ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 433) == ENOTCAPABLE); + caphp = cap_gethostbyname2(capnet, TEST_IPV4, AF_INET); + ATF_REQUIRE(caphp != NULL); + ATF_REQUIRE(caphp->h_addrtype == AF_INET); + ATF_REQUIRE(test_connect(capnet, "8.8.8.8", 433) == ENOTCAPABLE); + + for (i = 0; caphp->h_addr_list[i] != NULL; i++) { + int s; + + s = socket(AF_INET, SOCK_STREAM, 0); + ATF_REQUIRE(s >= 0); + + memset(&connaddr, 0, sizeof(connaddr)); + connaddr.sin_family = AF_INET; + memcpy((char *)&connaddr.sin_addr.s_addr, + (char *)caphp->h_addr_list[i], caphp->h_length); + connaddr.sin_port = htons(80); + + error = cap_connect(capnet, s, (struct sockaddr *)&connaddr, + sizeof(connaddr)); + if (error != 0 && errno != ENOTCAPABLE) + atf_tc_skip("unable to connect: %s", strerror(errno)); + ATF_REQUIRE(error == 0); + ATF_REQUIRE(close(s) == 0); + } + + cap_close(capnet); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, capnet__connect); + ATF_TP_ADD_TC(tp, capnet__bind); + ATF_TP_ADD_TC(tp, capnet__getnameinfo); + ATF_TP_ADD_TC(tp, capnet__getaddrinfo); + ATF_TP_ADD_TC(tp, capnet__gethostbyname); + ATF_TP_ADD_TC(tp, capnet__gethostbyaddr); + + ATF_TP_ADD_TC(tp, capnet__getnameinfo_buffer); + + ATF_TP_ADD_TC(tp, capnet__limits_addr2name_mode); + ATF_TP_ADD_TC(tp, capnet__limits_addr2name_family); + ATF_TP_ADD_TC(tp, capnet__limits_addr2name); + + ATF_TP_ADD_TC(tp, capnet__limits_deprecated_addr2name_mode); + ATF_TP_ADD_TC(tp, capnet__limits_deprecated_addr2name_family); + ATF_TP_ADD_TC(tp, capnet__limits_deprecated_addr2name); + + ATF_TP_ADD_TC(tp, capnet__limits_name2addr_mode); + ATF_TP_ADD_TC(tp, capnet__limits_name2addr_hosts); + ATF_TP_ADD_TC(tp, capnet__limits_name2addr_hosts_servnames_strict); + ATF_TP_ADD_TC(tp, capnet__limits_name2addr_hosts_servnames_mix); + ATF_TP_ADD_TC(tp, capnet__limits_name2addr_family); + + ATF_TP_ADD_TC(tp, capnet__limits_deprecated_name2addr_mode); + ATF_TP_ADD_TC(tp, capnet__limits_deprecated_name2addr_hosts); + ATF_TP_ADD_TC(tp, capnet__limits_deprecated_name2addr_family); + + ATF_TP_ADD_TC(tp, capnet__limits_bind_mode); + ATF_TP_ADD_TC(tp, capnet__limits_bind); + + ATF_TP_ADD_TC(tp, capnet__limits_connect_mode); + ATF_TP_ADD_TC(tp, capnet__limits_connect_dns_mode); + ATF_TP_ADD_TC(tp, capnet__limits_connect); + + ATF_TP_ADD_TC(tp, capnet__limits_connecttodns); + ATF_TP_ADD_TC(tp, capnet__limits_deprecated_connecttodns); + + return (atf_no_error()); +} |