Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F109032959
D24688.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
89 KB
Referenced Files
None
Subscribers
None
D24688.diff
View Options
Index: head/lib/libcasper/services/Makefile
===================================================================
--- head/lib/libcasper/services/Makefile
+++ head/lib/libcasper/services/Makefile
@@ -5,6 +5,7 @@
SUBDIR= cap_dns
SUBDIR+= cap_fileargs
SUBDIR+= cap_grp
+SUBDIR+= cap_net
SUBDIR+= cap_pwd
SUBDIR+= cap_sysctl
SUBDIR+= cap_syslog
Index: head/lib/libcasper/services/cap_dns/Makefile
===================================================================
--- head/lib/libcasper/services/cap_dns/Makefile
+++ head/lib/libcasper/services/cap_dns/Makefile
@@ -27,11 +27,6 @@
MAN+= cap_dns.3
MLINKS+=cap_dns.3 libcap_dns.3
-MLINKS+=cap_dns.3 cap_gethostbyname.3
-MLINKS+=cap_dns.3 cap_gethostbyname2.3
-MLINKS+=cap_dns.3 cap_gethostbyaddr.3
-MLINKS+=cap_dns.3 cap_getaddrinfo.3
-MLINKS+=cap_dns.3 cap_getnameinfo.3
MLINKS+=cap_dns.3 cap_dns_type_limit.3
MLINKS+=cap_dns.3 cap_dns_family_limit.3
Index: head/lib/libcasper/services/cap_dns/cap_dns.3
===================================================================
--- head/lib/libcasper/services/cap_dns/cap_dns.3
+++ head/lib/libcasper/services/cap_dns/cap_dns.3
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd May 5, 2020
+.Dd August 15, 2020
.Dt CAP_DNS 3
.Os
.Sh NAME
@@ -58,6 +58,9 @@
.Fn cap_dns_family_limit "const cap_channel_t *chan" "const int *families" "size_t nfamilies"
.Sh DESCRIPTION
.Bf -symbolic
+This service is obsolete and
+.Xr cap_net 3
+should be used instead.
The
.Fn cap_getaddrinfo ,
and
Index: head/lib/libcasper/services/cap_net/Makefile
===================================================================
--- head/lib/libcasper/services/cap_net/Makefile
+++ head/lib/libcasper/services/cap_net/Makefile
@@ -0,0 +1,48 @@
+# $FreeBSD$
+
+SHLIBDIR?= /lib/casper
+
+.include <src.opts.mk>
+
+PACKAGE=libcasper
+
+SHLIB_MAJOR= 1
+INCSDIR?= ${INCLUDEDIR}/casper
+
+.if ${MK_CASPER} != "no"
+SHLIB= cap_net
+
+SRCS= cap_net.c
+.endif
+
+INCS= cap_net.h
+
+LIBADD= nv
+
+CFLAGS+=-I${.CURDIR}
+CFLAGS+=-DWITH_CASPER
+
+HAS_TESTS=
+SUBDIR.${MK_TESTS}+= tests
+
+MAN+= cap_net.3
+
+MLINKS+=cap_net.3 libcap_net.3
+MLINKS+=cap_net.3 cap_bind.3
+MLINKS+=cap_net.3 cap_connect.3
+MLINKS+=cap_net.3 cap_net_free.3
+MLINKS+=cap_net.3 cap_net_limit.3
+MLINKS+=cap_net.3 cap_net_limit_addr2name.3
+MLINKS+=cap_net.3 cap_net_limit_addr2name_family.3
+MLINKS+=cap_net.3 cap_net_limit_bind.3
+MLINKS+=cap_net.3 cap_net_limit_connect.3
+MLINKS+=cap_net.3 cap_net_limit_init.3
+MLINKS+=cap_net.3 cap_net_limit_name2addr.3
+MLINKS+=cap_net.3 cap_net_limit_name2addr_family.3
+MLINKS+=cap_net.3 cap_getaddrinfo.3
+MLINKS+=cap_net.3 cap_gethostbyaddr.3
+MLINKS+=cap_net.3 cap_gethostbyname.3
+MLINKS+=cap_net.3 cap_gethostbyname2.3
+MLINKS+=cap_net.3 cap_getnameinfo.3
+
+.include <bsd.lib.mk>
Index: head/lib/libcasper/services/cap_net/cap_net.h
===================================================================
--- head/lib/libcasper/services/cap_net/cap_net.h
+++ head/lib/libcasper/services/cap_net/cap_net.h
@@ -0,0 +1,165 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * 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 AUTHORS 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 AUTHORS 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _CAP_NETWORK_H_
+#define _CAP_NETWORK_H_
+
+#ifdef HAVE_CASPER
+#define WITH_CASPER
+#endif
+
+#include <sys/dnv.h>
+#include <sys/nv.h>
+
+#include <sys/socket.h>
+
+struct addrinfo;
+struct hostent;
+
+struct cap_net_limit;
+typedef struct cap_net_limit cap_net_limit_t;
+
+#define CAPNET_ADDR2NAME (0x01)
+#define CAPNET_NAME2ADDR (0x02)
+#define CAPNET_DEPRECATED_ADDR2NAME (0x04)
+#define CAPNET_DEPRECATED_NAME2ADDR (0x08)
+#define CAPNET_CONNECT (0x10)
+#define CAPNET_BIND (0x20)
+#define CAPNET_CONNECTDNS (0x40)
+
+#ifdef WITH_CASPER
+/* Capability functions. */
+int cap_bind(cap_channel_t *chan, int s, const struct sockaddr *addr,
+ socklen_t addrlen);
+int cap_connect(cap_channel_t *chan, int s, const struct sockaddr *name,
+ socklen_t namelen);
+
+int cap_getaddrinfo(cap_channel_t *chan, const char *hostname,
+ const char *servname, const struct addrinfo *hints, struct addrinfo **res);
+int cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa,
+ socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen,
+ int flags);
+
+/* Limit functions. */
+cap_net_limit_t *cap_net_limit_init(cap_channel_t *chan, uint64_t mode);
+int cap_net_limit(cap_net_limit_t *limit);
+void cap_net_free(cap_net_limit_t *limit);
+
+cap_net_limit_t *cap_net_limit_addr2name_family(cap_net_limit_t *limit,
+ int *family, size_t size);
+cap_net_limit_t *cap_net_limit_addr2name(cap_net_limit_t *limit,
+ const struct sockaddr *sa, socklen_t salen);
+
+cap_net_limit_t *cap_net_limit_name2addr_family(cap_net_limit_t *limit,
+ int *family, size_t size);
+cap_net_limit_t *cap_net_limit_name2addr(cap_net_limit_t *limit,
+ const char *name, const char *serv);
+
+cap_net_limit_t *cap_net_limit_connect(cap_net_limit_t *limit,
+ const struct sockaddr *sa, socklen_t salen);
+
+cap_net_limit_t *cap_net_limit_bind(cap_net_limit_t *limit,
+ const struct sockaddr *sa, socklen_t salen);
+
+/* Deprecated functions. */
+struct hostent *cap_gethostbyname(cap_channel_t *chan, const char *name);
+struct hostent *cap_gethostbyname2(cap_channel_t *chan, const char *name,
+ int af);
+struct hostent *cap_gethostbyaddr(cap_channel_t *chan, const void *addr,
+ socklen_t len, int af);
+#else
+/* Capability functions. */
+#define cap_bind(chan, s, addr, addrlen) \
+ bind(s, addr, addrlen)
+#define cap_connect(chan, s, name, namelen) \
+ connect(s, name, namelen)
+#define cap_getaddrinfo(chan, hostname, servname, hints, res) \
+ getaddrinfo(hostname, servname, hints, res)
+#define cap_getnameinfo(chan, sa, salen, host, hostlen, serv, servlen, flags) \
+ getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
+
+/* Limit functions. */
+#define cap_net_limit_init(chan, mode) ((cap_net_limit_t *)malloc(8))
+#define cap_net_free(limit) free(limit)
+static inline int
+cap_net_limit(cap_net_limit_t *limit)
+{
+ free(limit);
+ return (0);
+}
+
+static inline cap_net_limit_t *
+cap_net_limit_addr2name_family(cap_net_limit_t *limit,
+ int *family __unused, size_t size __unused)
+{
+ return (limit);
+}
+
+static inline cap_net_limit_t *
+cap_net_limit_addr2name(cap_net_limit_t *limit,
+ const struct sockaddr *sa __unused, socklen_t salen __unused)
+{
+ return (limit);
+}
+
+static inline cap_net_limit_t *
+cap_net_limit_name2addr_family(cap_net_limit_t *limit,
+ int *family __unused, size_t size __unused)
+{
+ return (limit);
+}
+
+static inline cap_net_limit_t *
+cap_net_limit_name2addr(cap_net_limit_t *limit,
+ const char *name __unused, const char *serv __unused)
+{
+ return (limit);
+}
+
+static inline cap_net_limit_t *
+cap_net_limit_connect(cap_net_limit_t *limit,
+ const struct sockaddr *sa __unused, socklen_t salen __unused)
+{
+ return (limit);
+}
+
+static inline cap_net_limit_t *
+cap_net_limit_bind(cap_net_limit_t *limit,
+ const struct sockaddr *sa __unused, socklen_t salen __unused)
+{
+ return (limit);
+}
+
+/* Deprecated functions. */
+#define cap_gethostbyname(chan, name) gethostbyname(name)
+#define cap_gethostbyname2(chan, name, type) gethostbyname2(name, type)
+#define cap_gethostbyaddr(chan, addr, len, type) gethostbyaddr(addr, len, type)
+#endif
+
+#endif /* !_CAP_NETWORK_H_ */
Index: head/lib/libcasper/services/cap_net/cap_net.3
===================================================================
--- head/lib/libcasper/services/cap_net/cap_net.3
+++ head/lib/libcasper/services/cap_net/cap_net.3
@@ -0,0 +1,287 @@
+.\" 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 AUTHORS 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 AUTHORS 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 15, 2020
+.Dt CAP_NET 3
+.Os
+.Sh NAME
+.Nm cap_bind ,
+.Nm cap_connect ,
+.Nm cap_getaddrinfo ,
+.Nm cap_gethostbyaddr ,
+.Nm cap_gethostbyname ,
+.Nm cap_gethostbyname2 ,
+.Nm cap_getnameinfo ,
+.Nm cap_net_free ,
+.Nm cap_net_limit ,
+.Nm cap_net_limit_addr2name ,
+.Nm cap_net_limit_addr2name_family ,
+.Nm cap_net_limit_bind ,
+.Nm cap_net_limit_connect ,
+.Nm cap_net_limit_init ,
+.Nm cap_net_limit_name2addr ,
+.Nm cap_net_limit_name2addr_family ,
+.Nd "library for networking in capability mode"
+.Sh LIBRARY
+.Lb libcap_net
+.Sh SYNOPSIS
+.In sys/nv.h
+.In libcasper.h
+.In casper/cap_net.h
+.Ft int
+.Fn cap_bind "cap_channel_t *chan" "int s" "const struct sockaddr *addr" "socklen_t addrlen"
+.Ft int
+.Fn cap_connect "cap_channel_t *chan" "int s" "const struct sockaddr *name" "socklen_t namelen"
+.Ft int
+.Fn cap_getaddrinfo "cap_channel_t *chan" "const char *hostname" "const char *servname" "const struct addrinfo *hints" "struct addrinfo **res"
+.Ft int
+.Fn cap_getnameinfo "cap_channel_t *chan" "const struct sockaddr *sa" "socklen_t salen" "char *host" "size_t hostlen" "char *serv" "size_t servlen" "int flags"
+.Ft "struct hostent *"
+.Fn cap_gethostbyname "const cap_channel_t *chan" "const char *name"
+.Ft "struct hostent *"
+.Fn cap_gethostbyname2 "const cap_channel_t *chan" "const char *name" "int af"
+.Ft "struct hostent *"
+.Fn cap_gethostbyaddr "const cap_channel_t *chan" "const void *addr" "socklen_t len" "int af"
+.Ft "cap_net_limit_t *"
+.Fn cap_net_limit_init "cap_channel_t *chan" "uint64_t mode"
+.Ft int
+.Fn cap_net_limit "cap_net_limit_t *limit"
+.Ft void
+.Fn cap_net_free "cap_net_limit_t *limit"
+.Ft "cap_net_limit_t *"
+.Fn cap_net_limit_addr2name_family "cap_net_limit_t *limit" "int *family" "size_t size"
+.Ft "cap_net_limit_t *"
+.Fn cap_net_limit_addr2name "cap_net_limit_t *limit" "const struct sockaddr *sa" "socklen_t salen"
+.Ft "cap_net_limit_t *"
+.Fn cap_net_limit_name2addr_family "cap_net_limit_t *limit" "int *family" "size_t size"
+.Ft "cap_net_limit_t *"
+.Fn cap_net_limit_name2addr "cap_net_limit_t *limit" "const char *name" "const char *serv"
+.Ft "cap_net_limit_t *"
+.Fn cap_net_limit_connect "cap_net_limit_t *limit" "const struct sockaddr *sa" "socklen_t salen"
+.Ft "cap_net_limit_t *"
+.Fn cap_net_limit_bind "cap_net_limit_t *limit" "const struct sockaddr *sa" "socklen_t salen"
+.Sh DESCRIPTION
+.Pp
+The functions
+.Fn cap_bind,
+.Fn cap_connect,
+.Fn cap_gethostbyname ,
+.Fn cap_gethostbyname2 ,
+.Fn cap_gethostbyaddr
+and
+.Fn cap_getnameinfo
+are respectively equivalent to
+.Xr bind 2 ,
+.Xr connect 2 ,
+.Xr gethostbyname 3 ,
+.Xr gethostbyname2 3 ,
+.Xr gethostbyaddr 3
+and
+.Xr getnameinfo 3
+except that the connection to the
+.Nm system.net
+service needs to be provided.
+.Sh LIMITS
+By default, the cap_net capability provides unrestricted access to the network
+namespace.
+Applications typically only require access to a small portion of the network
+namespace:
+.Fn cap_net_limit
+interface can be used to restrict access to the network.
+.Fn cap_net_limit_init
+returns an opaque limit handle used to store a list of capabilities.
+The
+.Fv mode
+restricts the functionality of the service.
+Modes are encoded using the following flags:
+.Pp
+.Bd -literal -offset indent -compact
+CAPNET_ADDR2NAME reverse DNS lookups are allowed with
+ cap_getnameinfo
+CAPNET_NAME2ADDR name resolution is allowed with
+ cap_getaddrinfo
+CAPNET_DEPRECATED_ADDR2NAME reverse DNS lookups are allowed with
+ cap_gethostbyaddr
+CAPNET_DEPRECATED_NAME2ADDR name resolution is allowed with
+ cap_gethostbyname and cap_gethostbyname2
+CAPNET_BIND bind syscall is allowed
+CAPNET_CONNECT connect syscall is allowed
+CAPNET_CONNECTDNS connect syscall is allowed to the values
+ returned from privies call to
+ the cap_getaddrinfo or cap_gethostbyname
+.Ed
+.Pp
+.Fn cap_net_limit_addr2name_family
+limits the
+.Fn cap_getnameinfo
+and
+.Fn cap_gethostbyaddr
+to do reverse DNS lookups to specific family (AF_INET, AF_INET6, etc.)
+.Pp
+.Fn cap_net_limit_addr2name
+limits the
+.Fn cap_getnameinfo
+and
+.Fn cap_gethostbyaddr
+to do reverse DNS lookups only on those specific structures.
+.Pp
+.Fn cap_net_limit_name2addr_family
+limits the
+.Fn cap_getaddrinfo ,
+.Fn cap_gethostbyname
+and
+.Fn cap_gethostbyname2
+to do the name resolution on specific family (AF_INET, AF_INET6, etc.)
+.Pp
+.Fn cap_net_limit_addr2name
+restricts
+.Fn cap_getaddrinfo ,
+.Fn cap_gethostbyname
+and
+.Fn cap_gethostbyname2
+to a set of domains.
+.Pp
+.Fn cap_net_limit_bind
+limits
+.Fn cap_bind
+to bind only on those specific structures.
+.Pp
+.Fn cap_net_limit_connect
+limits
+.Fn cap_connect
+to connect only on those specific structures.
+If the CAPNET_CONNECTDNS is set the limits are extended to the values returned
+by
+.Fn cap_getaddrinfo ,
+.Fn cap_gethostbyname
+and
+.Fn cap_gethostbyname2
+In case of the
+.Fn cap_getaddrinfo
+the restriction is strict.
+In case of the
+.Fn cap_gethostbyname
+and
+.Fn cap_gethostbyname2
+any port will be accepted in the
+.Fn cap_connect
+function.
+.Pp
+.Fn cap_net_limit
+applies a set of sysctl limits to the capability, denying access to sysctl
+variables not belonging to the set.
+.Pp
+Once a set of limits is applied, subsequent calls to
+.Fn cap_net_limit
+will fail unless the new set is a subset of the current set.
+.Pp
+The
+.Fn cap_net_limit
+will consume the limits.
+If the
+.Fn cap_net_limit
+was not called the rights may be freed using
+.Fn cap_net_free .
+Multiple calls to
+.Fn cap_net_limit_addr2name_family ,
+.Fn cap_net_limit_addr2name ,
+.Fn cap_net_limit_name2addr_family ,
+.Fn cap_net_limit_name2addr ,
+.Fn cap_net_limit_connect ,
+and
+.Fn cap_net_limit_bind
+is supported, each call is extending preview capabilities.
+.Sh EXAMPLES
+The following example first opens a capability to casper and then uses this
+capability to create the
+.Nm system.net
+casper service and uses it to resolve a host and connect to it.
+.Bd -literal
+cap_channel_t *capcas, *capnet;
+cap_net_limit_t *limit;
+int familylimit, error, s;
+const char *host = "example.com";
+struct addrinfo hints, *res;
+
+/* Open capability to Casper. */
+capcas = cap_init();
+if (capcas == NULL)
+ err(1, "Unable to contact Casper");
+
+/* Cache NLA for gai_strerror. */
+caph_cache_catpages();
+
+/* Enter capability mode sandbox. */
+if (caph_enter_casper() < 0)
+ err(1, "Unable to enter capability mode");
+
+/* Use Casper capability to create capability to the system.net service. */
+capnet = cap_service_open(capcas, "system.net");
+if (capnet == NULL)
+ err(1, "Unable to open system.net service");
+
+/* Close Casper capability. */
+cap_close(capcas);
+
+/* Limit system.net to reserve IPv4 addresses, to host example.com . */
+limit = cap_net_limit_init(capnet, CAPNET_NAME2ADDR | CAPNET_CONNECTDNS);
+if (limit == NULL)
+ err(1, "Unable to create limits.");
+cap_net_limit_name2addr(limit, host, "80");
+familylimit = AF_INET;
+cap_net_limit_name2addr_family(limit, &familylimit, 1);
+if (cap_net_limit(limit) < 0)
+ err(1, "Unable to apply limits.");
+
+/* Find IP addresses for the given host. */
+memset(&hints, 0, sizeof(hints));
+hints.ai_family = AF_INET;
+hints.ai_socktype = SOCK_STREAM;
+
+error = cap_getaddrinfo(capnet, host, "80", &hints, &res);
+if (error != 0)
+ errx(1, "cap_getaddrinfo(): %s: %s", host, gai_strerror(error));
+
+s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+if (s < 0)
+ err(1, "Unable to create socket");
+
+if (cap_connect(capnet, s, res->ai_addr, res->ai_addrlen) < 0)
+ err(1, "Unable to connect to host");
+.Ed
+.Sh SEE ALSO
+.Xr bind 2 ,
+.Xr cap_enter 2 ,
+.Xr connect 2 ,
+.Xr caph_enter 3 ,
+.Xr err 3 ,
+.Xr gethostbyaddr 3 ,
+.Xr gethostbyname 3 ,
+.Xr gethostbyname2 3 ,
+.Xr getnameinfo 3 ,
+.Xr capsicum 4 ,
+.Xr nv 9
+.Sh AUTHORS
+.An Mariusz Zaborski Aq Mt oshogbo@FreeBSD.org
Index: head/lib/libcasper/services/cap_net/cap_net.c
===================================================================
--- head/lib/libcasper/services/cap_net/cap_net.c
+++ head/lib/libcasper/services/cap_net/cap_net.c
@@ -0,0 +1,1385 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * 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 AUTHORS 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 AUTHORS 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/cnv.h>
+#include <sys/dnv.h>
+#include <sys/nv.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libcasper.h>
+#include <libcasper_service.h>
+
+#include "cap_net.h"
+
+#define CAPNET_MASK (CAPNET_ADDR2NAME | CAPNET_NAME2ADDR \
+ CAPNET_DEPRECATED_ADDR2NAME | CAPNET_DEPRECATED_NAME2ADDR | \
+ CAPNET_CONNECT | CAPNET_BIND | CAPNET_CONNECTDNS)
+
+/*
+ * Defines for the names of the limits.
+ * XXX: we should convert all string constats to this to avoid typos.
+ */
+#define LIMIT_NV_BIND "bind"
+#define LIMIT_NV_CONNECT "connect"
+#define LIMIT_NV_ADDR2NAME "addr2name"
+#define LIMIT_NV_NAME2ADDR "name2addr"
+
+struct cap_net_limit {
+ cap_channel_t *cnl_chan;
+ uint64_t cnl_mode;
+ nvlist_t *cnl_addr2name;
+ nvlist_t *cnl_name2addr;
+ nvlist_t *cnl_connect;
+ nvlist_t *cnl_bind;
+};
+
+static struct hostent hent;
+
+static void
+hostent_free(struct hostent *hp)
+{
+ unsigned int ii;
+
+ free(hp->h_name);
+ hp->h_name = NULL;
+ if (hp->h_aliases != NULL) {
+ for (ii = 0; hp->h_aliases[ii] != NULL; ii++)
+ free(hp->h_aliases[ii]);
+ free(hp->h_aliases);
+ hp->h_aliases = NULL;
+ }
+ if (hp->h_addr_list != NULL) {
+ for (ii = 0; hp->h_addr_list[ii] != NULL; ii++)
+ free(hp->h_addr_list[ii]);
+ free(hp->h_addr_list);
+ hp->h_addr_list = NULL;
+ }
+}
+
+static struct hostent *
+hostent_unpack(const nvlist_t *nvl, struct hostent *hp)
+{
+ unsigned int ii, nitems;
+ char nvlname[64];
+ int n;
+
+ hostent_free(hp);
+
+ hp->h_name = strdup(nvlist_get_string(nvl, "name"));
+ if (hp->h_name == NULL)
+ goto fail;
+ hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype");
+ hp->h_length = (int)nvlist_get_number(nvl, "length");
+
+ nitems = (unsigned int)nvlist_get_number(nvl, "naliases");
+ hp->h_aliases = calloc(sizeof(hp->h_aliases[0]), nitems + 1);
+ if (hp->h_aliases == NULL)
+ goto fail;
+ for (ii = 0; ii < nitems; ii++) {
+ n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
+ assert(n > 0 && n < (int)sizeof(nvlname));
+ hp->h_aliases[ii] =
+ strdup(nvlist_get_string(nvl, nvlname));
+ if (hp->h_aliases[ii] == NULL)
+ goto fail;
+ }
+ hp->h_aliases[ii] = NULL;
+
+ nitems = (unsigned int)nvlist_get_number(nvl, "naddrs");
+ hp->h_addr_list = calloc(sizeof(hp->h_addr_list[0]), nitems + 1);
+ if (hp->h_addr_list == NULL)
+ goto fail;
+ for (ii = 0; ii < nitems; ii++) {
+ hp->h_addr_list[ii] = malloc(hp->h_length);
+ if (hp->h_addr_list[ii] == NULL)
+ goto fail;
+ n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
+ assert(n > 0 && n < (int)sizeof(nvlname));
+ bcopy(nvlist_get_binary(nvl, nvlname, NULL),
+ hp->h_addr_list[ii], hp->h_length);
+ }
+ hp->h_addr_list[ii] = NULL;
+
+ return (hp);
+fail:
+ hostent_free(hp);
+ h_errno = NO_RECOVERY;
+ return (NULL);
+}
+
+static int
+request_cb(cap_channel_t *chan, const char *name, int s,
+ const struct sockaddr *saddr, socklen_t len)
+{
+ nvlist_t *nvl;
+ int serrno;
+
+ nvl = nvlist_create(0);
+ nvlist_add_string(nvl, "cmd", name);
+ nvlist_add_descriptor(nvl, "s", s);
+ nvlist_add_binary(nvl, "saddr", saddr, len);
+
+ nvl = cap_xfer_nvlist(chan, nvl);
+ if (nvl == NULL)
+ return (-1);
+
+ if (nvlist_get_number(nvl, "error") != 0) {
+ serrno = (int)nvlist_get_number(nvl, "error");
+ nvlist_destroy(nvl);
+ errno = serrno;
+ return (-1);
+ }
+
+ s = dup2(s, nvlist_get_descriptor(nvl, "s"));
+ nvlist_destroy(nvl);
+
+ return (s == -1 ? -1 : 0);
+}
+
+int
+cap_bind(cap_channel_t *chan, int s, const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+
+ return (request_cb(chan, LIMIT_NV_BIND, s, addr, addrlen));
+}
+
+int
+cap_connect(cap_channel_t *chan, int s, const struct sockaddr *name,
+ socklen_t namelen)
+{
+
+ return (request_cb(chan, LIMIT_NV_CONNECT, s, name, namelen));
+}
+
+
+struct hostent *
+cap_gethostbyname(cap_channel_t *chan, const char *name)
+{
+
+ return (cap_gethostbyname2(chan, name, AF_INET));
+}
+
+struct hostent *
+cap_gethostbyname2(cap_channel_t *chan, const char *name, int af)
+{
+ struct hostent *hp;
+ nvlist_t *nvl;
+
+ nvl = nvlist_create(0);
+ nvlist_add_string(nvl, "cmd", "gethostbyname");
+ nvlist_add_number(nvl, "family", (uint64_t)af);
+ nvlist_add_string(nvl, "name", name);
+ nvl = cap_xfer_nvlist(chan, nvl);
+ if (nvl == NULL) {
+ h_errno = NO_RECOVERY;
+ return (NULL);
+ }
+ if (nvlist_get_number(nvl, "error") != 0) {
+ h_errno = (int)nvlist_get_number(nvl, "error");
+ nvlist_destroy(nvl);
+ return (NULL);
+ }
+
+ hp = hostent_unpack(nvl, &hent);
+ nvlist_destroy(nvl);
+ return (hp);
+}
+
+struct hostent *
+cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len,
+ int af)
+{
+ struct hostent *hp;
+ nvlist_t *nvl;
+
+ nvl = nvlist_create(0);
+ nvlist_add_string(nvl, "cmd", "gethostbyaddr");
+ nvlist_add_binary(nvl, "addr", addr, (size_t)len);
+ nvlist_add_number(nvl, "family", (uint64_t)af);
+ nvl = cap_xfer_nvlist(chan, nvl);
+ if (nvl == NULL) {
+ h_errno = NO_RECOVERY;
+ return (NULL);
+ }
+ if (nvlist_get_number(nvl, "error") != 0) {
+ h_errno = (int)nvlist_get_number(nvl, "error");
+ nvlist_destroy(nvl);
+ return (NULL);
+ }
+ hp = hostent_unpack(nvl, &hent);
+ nvlist_destroy(nvl);
+ return (hp);
+}
+
+static struct addrinfo *
+addrinfo_unpack(const nvlist_t *nvl)
+{
+ struct addrinfo *ai;
+ const void *addr;
+ size_t addrlen;
+ const char *canonname;
+
+ addr = nvlist_get_binary(nvl, "ai_addr", &addrlen);
+ ai = malloc(sizeof(*ai) + addrlen);
+ if (ai == NULL)
+ return (NULL);
+ ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags");
+ ai->ai_family = (int)nvlist_get_number(nvl, "ai_family");
+ ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype");
+ ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol");
+ ai->ai_addrlen = (socklen_t)addrlen;
+ canonname = dnvlist_get_string(nvl, "ai_canonname", NULL);
+ if (canonname != NULL) {
+ ai->ai_canonname = strdup(canonname);
+ if (ai->ai_canonname == NULL) {
+ free(ai);
+ return (NULL);
+ }
+ } else {
+ ai->ai_canonname = NULL;
+ }
+ ai->ai_addr = (void *)(ai + 1);
+ bcopy(addr, ai->ai_addr, addrlen);
+ ai->ai_next = NULL;
+
+ return (ai);
+}
+
+int
+cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res)
+{
+ struct addrinfo *firstai, *prevai, *curai;
+ unsigned int ii;
+ const nvlist_t *nvlai;
+ char nvlname[64];
+ nvlist_t *nvl;
+ int error, n;
+
+ nvl = nvlist_create(0);
+ nvlist_add_string(nvl, "cmd", "getaddrinfo");
+ if (hostname != NULL)
+ nvlist_add_string(nvl, "hostname", hostname);
+ if (servname != NULL)
+ nvlist_add_string(nvl, "servname", servname);
+ if (hints != NULL) {
+ nvlist_add_number(nvl, "hints.ai_flags",
+ (uint64_t)hints->ai_flags);
+ nvlist_add_number(nvl, "hints.ai_family",
+ (uint64_t)hints->ai_family);
+ nvlist_add_number(nvl, "hints.ai_socktype",
+ (uint64_t)hints->ai_socktype);
+ nvlist_add_number(nvl, "hints.ai_protocol",
+ (uint64_t)hints->ai_protocol);
+ }
+ nvl = cap_xfer_nvlist(chan, nvl);
+ if (nvl == NULL)
+ return (EAI_MEMORY);
+ if (nvlist_get_number(nvl, "error") != 0) {
+ error = (int)nvlist_get_number(nvl, "error");
+ nvlist_destroy(nvl);
+ return (error);
+ }
+
+ nvlai = NULL;
+ firstai = prevai = curai = NULL;
+ for (ii = 0; ; ii++) {
+ n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
+ assert(n > 0 && n < (int)sizeof(nvlname));
+ if (!nvlist_exists_nvlist(nvl, nvlname))
+ break;
+ nvlai = nvlist_get_nvlist(nvl, nvlname);
+ curai = addrinfo_unpack(nvlai);
+ if (curai == NULL)
+ return (EAI_MEMORY);
+ if (prevai != NULL)
+ prevai->ai_next = curai;
+ else
+ firstai = curai;
+ prevai = curai;
+ }
+ nvlist_destroy(nvl);
+ if (curai == NULL && nvlai != NULL) {
+ if (firstai == NULL)
+ freeaddrinfo(firstai);
+ return (EAI_MEMORY);
+ }
+
+ *res = firstai;
+ return (0);
+}
+
+int
+cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen,
+ char *host, size_t hostlen, char *serv, size_t servlen, int flags)
+{
+ nvlist_t *nvl;
+ int error;
+
+ nvl = nvlist_create(0);
+ nvlist_add_string(nvl, "cmd", "getnameinfo");
+ nvlist_add_number(nvl, "hostlen", (uint64_t)hostlen);
+ nvlist_add_number(nvl, "servlen", (uint64_t)servlen);
+ nvlist_add_binary(nvl, "sa", sa, (size_t)salen);
+ nvlist_add_number(nvl, "flags", (uint64_t)flags);
+ nvl = cap_xfer_nvlist(chan, nvl);
+ if (nvl == NULL)
+ return (EAI_MEMORY);
+ if (nvlist_get_number(nvl, "error") != 0) {
+ error = (int)nvlist_get_number(nvl, "error");
+ nvlist_destroy(nvl);
+ return (error);
+ }
+
+ if (host != NULL && nvlist_exists_string(nvl, "host"))
+ strlcpy(host, nvlist_get_string(nvl, "host"), hostlen + 1);
+ if (serv != NULL && nvlist_exists_string(nvl, "serv"))
+ strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen + 1);
+ nvlist_destroy(nvl);
+ return (0);
+}
+
+cap_net_limit_t *
+cap_net_limit_init(cap_channel_t *chan, uint64_t mode)
+{
+ cap_net_limit_t *limit;
+
+ limit = calloc(1, sizeof(*limit));
+ if (limit != NULL) {
+ limit->cnl_mode = mode;
+ limit->cnl_chan = chan;
+ limit->cnl_addr2name = nvlist_create(0);
+ limit->cnl_name2addr = nvlist_create(0);
+ limit->cnl_connect = nvlist_create(0);
+ limit->cnl_bind = nvlist_create(0);
+ }
+
+ return (limit);
+}
+
+static void
+pack_limit(nvlist_t *lnvl, const char *name, nvlist_t *limit)
+{
+
+ if (!nvlist_empty(limit)) {
+ nvlist_move_nvlist(lnvl, name, limit);
+ } else {
+ nvlist_destroy(limit);
+ }
+}
+
+int
+cap_net_limit(cap_net_limit_t *limit)
+{
+ nvlist_t *lnvl;
+ cap_channel_t *chan;
+
+ lnvl = nvlist_create(0);
+ nvlist_add_number(lnvl, "mode", limit->cnl_mode);
+
+ pack_limit(lnvl, LIMIT_NV_ADDR2NAME, limit->cnl_addr2name);
+ pack_limit(lnvl, LIMIT_NV_NAME2ADDR, limit->cnl_name2addr);
+ pack_limit(lnvl, LIMIT_NV_CONNECT, limit->cnl_connect);
+ pack_limit(lnvl, LIMIT_NV_BIND, limit->cnl_bind);
+
+ chan = limit->cnl_chan;
+ free(limit);
+
+ return (cap_limit_set(chan, lnvl));
+}
+
+void
+cap_net_free(cap_net_limit_t *limit)
+{
+
+ if (limit == NULL)
+ return;
+
+ nvlist_destroy(limit->cnl_addr2name);
+ nvlist_destroy(limit->cnl_name2addr);
+ nvlist_destroy(limit->cnl_connect);
+ nvlist_destroy(limit->cnl_bind);
+
+ free(limit);
+}
+
+static void
+pack_family(nvlist_t *nvl, int *family, size_t size)
+{
+ size_t i;
+
+ i = 0;
+ if (!nvlist_exists_number_array(nvl, "family")) {
+ uint64_t val;
+
+ val = family[0];
+ nvlist_add_number_array(nvl, "family", &val, 1);
+ i += 1;
+ }
+
+ for (; i < size; i++) {
+ nvlist_append_number_array(nvl, "family", family[i]);
+ }
+}
+
+static void
+pack_sockaddr(nvlist_t *res, const struct sockaddr *sa, socklen_t salen)
+{
+ nvlist_t *nvl;
+
+ if (!nvlist_exists_nvlist(res, "sockaddr")) {
+ nvl = nvlist_create(NV_FLAG_NO_UNIQUE);
+ } else {
+ nvl = nvlist_take_nvlist(res, "sockaddr");
+ }
+
+ nvlist_add_binary(nvl, "", sa, salen);
+ nvlist_move_nvlist(res, "sockaddr", nvl);
+}
+
+cap_net_limit_t *
+cap_net_limit_addr2name_family(cap_net_limit_t *limit, int *family, size_t size)
+{
+
+ pack_family(limit->cnl_addr2name, family, size);
+ return (limit);
+}
+
+cap_net_limit_t *
+cap_net_limit_name2addr_family(cap_net_limit_t *limit, int *family, size_t size)
+{
+
+ pack_family(limit->cnl_name2addr, family, size);
+ return (limit);
+}
+
+cap_net_limit_t *
+cap_net_limit_name2addr(cap_net_limit_t *limit, const char *host,
+ const char *serv)
+{
+ nvlist_t *nvl;
+
+ if (!nvlist_exists_nvlist(limit->cnl_name2addr, "hosts")) {
+ nvl = nvlist_create(NV_FLAG_NO_UNIQUE);
+ } else {
+ nvl = nvlist_take_nvlist(limit->cnl_name2addr, "hosts");
+ }
+
+ nvlist_add_string(nvl,
+ host != NULL ? host : "",
+ serv != NULL ? serv : "");
+
+ nvlist_move_nvlist(limit->cnl_name2addr, "hosts", nvl);
+ return (limit);
+}
+
+cap_net_limit_t *
+cap_net_limit_addr2name(cap_net_limit_t *limit, const struct sockaddr *sa,
+ socklen_t salen)
+{
+
+ pack_sockaddr(limit->cnl_addr2name, sa, salen);
+ return (limit);
+}
+
+
+cap_net_limit_t *
+cap_net_limit_connect(cap_net_limit_t *limit, const struct sockaddr *sa,
+ socklen_t salen)
+{
+
+ pack_sockaddr(limit->cnl_connect, sa, salen);
+ return (limit);
+}
+
+cap_net_limit_t *
+cap_net_limit_bind(cap_net_limit_t *limit, const struct sockaddr *sa,
+ socklen_t salen)
+{
+
+ pack_sockaddr(limit->cnl_bind, sa, salen);
+ return (limit);
+}
+
+/*
+ * Service functions.
+ */
+
+static nvlist_t *capdnscache;
+
+static void
+net_add_sockaddr_to_cache(struct sockaddr *sa, socklen_t salen, bool deprecated)
+{
+ void *cookie;
+
+ if (capdnscache == NULL) {
+ capdnscache = nvlist_create(NV_FLAG_NO_UNIQUE);
+ } else {
+ /* Lets keep it clean. Look for dups. */
+ cookie = NULL;
+ while (nvlist_next(capdnscache, NULL, &cookie) != NULL) {
+ const void *data;
+ size_t size;
+
+ assert(cnvlist_type(cookie) == NV_TYPE_BINARY);
+
+ data = cnvlist_get_binary(cookie, &size);
+ if (salen != size)
+ continue;
+ if (memcmp(data, sa, size) == 0)
+ return;
+ }
+ }
+
+ nvlist_add_binary(capdnscache, deprecated ? "d" : "", sa, salen);
+}
+
+static void
+net_add_hostent_to_cache(const char *address, size_t asize, int family)
+{
+
+ if (family != AF_INET && family != AF_INET6)
+ return;
+
+ if (family == AF_INET6) {
+ struct sockaddr_in6 connaddr;
+
+ memset(&connaddr, 0, sizeof(connaddr));
+ connaddr.sin6_family = AF_INET6;
+ memcpy((char *)&connaddr.sin6_addr, address, asize);
+ connaddr.sin6_port = 0;
+
+ net_add_sockaddr_to_cache((struct sockaddr *)&connaddr,
+ sizeof(connaddr), true);
+ } else {
+ struct sockaddr_in connaddr;
+
+ memset(&connaddr, 0, sizeof(connaddr));
+ connaddr.sin_family = AF_INET;
+ memcpy((char *)&connaddr.sin_addr.s_addr, address, asize);
+ connaddr.sin_port = 0;
+
+ net_add_sockaddr_to_cache((struct sockaddr *)&connaddr,
+ sizeof(connaddr), true);
+ }
+}
+
+static bool
+net_allowed_mode(const nvlist_t *limits, uint64_t mode)
+{
+
+ if (limits == NULL)
+ return (true);
+
+ return ((nvlist_get_number(limits, "mode") & mode) == mode);
+}
+
+static bool
+net_allowed_family(const nvlist_t *limits, int family)
+{
+ const uint64_t *allowedfamily;
+ size_t i, allsize;
+
+ if (limits == NULL)
+ return (true);
+
+ /* If there are no familes at all, allow any mode. */
+ if (!nvlist_exists_number_array(limits, "family"))
+ return (true);
+
+ allowedfamily = nvlist_get_number_array(limits, "family", &allsize);
+ for (i = 0; i < allsize; i++) {
+ /* XXX: what with AF_UNSPEC? */
+ if (allowedfamily[i] == (uint64_t)family) {
+ return (true);
+ }
+ }
+
+ return (false);
+}
+
+static bool
+net_allowed_bsaddr_impl(const nvlist_t *salimits, const void *saddr,
+ size_t saddrsize)
+{
+ void *cookie;
+ const void *limit;
+ size_t limitsize;
+
+ cookie = NULL;
+ while (nvlist_next(salimits, NULL, &cookie) != NULL) {
+ limit = cnvlist_get_binary(cookie, &limitsize);
+
+ if (limitsize != saddrsize) {
+ continue;
+ }
+ if (memcmp(limit, saddr, limitsize) == 0) {
+ return (true);
+ }
+
+ /*
+ * In case of deprecated version (gethostbyname) we have to
+ * ignore port, because there is no such info in the hostent.
+ * Suporting only AF_INET and AF_INET6.
+ */
+ if (strcmp(cnvlist_name(cookie), "d") != 0 ||
+ (saddrsize != sizeof(struct sockaddr_in) &&
+ saddrsize != sizeof(struct sockaddr_in6))) {
+ continue;
+ }
+ if (saddrsize == sizeof(struct sockaddr_in)) {
+ const struct sockaddr_in *saddrptr;
+ struct sockaddr_in sockaddr;
+
+ saddrptr = (const struct sockaddr_in *)saddr;
+ memcpy(&sockaddr, limit, sizeof(sockaddr));
+ sockaddr.sin_port = saddrptr->sin_port;
+
+ if (memcmp(&sockaddr, saddr, saddrsize) == 0) {
+ return (true);
+ }
+ } else if (saddrsize == sizeof(struct sockaddr_in6)) {
+ const struct sockaddr_in6 *saddrptr;
+ struct sockaddr_in6 sockaddr;
+
+ saddrptr = (const struct sockaddr_in6 *)saddr;
+ memcpy(&sockaddr, limit, sizeof(sockaddr));
+ sockaddr.sin6_port = saddrptr->sin6_port;
+
+ if (memcmp(&sockaddr, saddr, saddrsize) == 0) {
+ return (true);
+ }
+ }
+ }
+
+ return (false);
+}
+
+static bool
+net_allowed_bsaddr(const nvlist_t *limits, const void *saddr, size_t saddrsize)
+{
+
+ if (limits == NULL)
+ return (true);
+
+ if (!nvlist_exists_nvlist(limits, "sockaddr"))
+ return (true);
+
+ return (net_allowed_bsaddr_impl(nvlist_get_nvlist(limits, "sockaddr"),
+ saddr, saddrsize));
+}
+
+static bool
+net_allowed_hosts(const nvlist_t *limits, const char *name, const char *srvname)
+{
+ void *cookie;
+ const nvlist_t *hlimits;
+ const char *testname, *testsrvname;
+
+ if (limits == NULL) {
+ return (true);
+ }
+
+ /* If there are no hosts at all, allow any. */
+ if (!nvlist_exists_nvlist(limits, "hosts")) {
+ return (true);
+ }
+
+ cookie = NULL;
+ testname = (name == NULL ? "" : name);
+ testsrvname = (srvname == NULL ? "" : srvname);
+ hlimits = nvlist_get_nvlist(limits, "hosts");
+ while (nvlist_next(hlimits, NULL, &cookie) != NULL) {
+ if (strcmp(cnvlist_name(cookie), "") != 0 &&
+ strcmp(cnvlist_name(cookie), testname) != 0) {
+ continue;
+ }
+
+ if (strcmp(cnvlist_get_string(cookie), "") != 0 &&
+ strcmp(cnvlist_get_string(cookie), testsrvname) != 0) {
+ continue;
+ }
+
+ return (true);
+ }
+
+ return (false);
+}
+
+static void
+hostent_pack(const struct hostent *hp, nvlist_t *nvl, bool addtocache)
+{
+ unsigned int ii;
+ char nvlname[64];
+ int n;
+
+ nvlist_add_string(nvl, "name", hp->h_name);
+ nvlist_add_number(nvl, "addrtype", (uint64_t)hp->h_addrtype);
+ nvlist_add_number(nvl, "length", (uint64_t)hp->h_length);
+
+ if (hp->h_aliases == NULL) {
+ nvlist_add_number(nvl, "naliases", 0);
+ } else {
+ for (ii = 0; hp->h_aliases[ii] != NULL; ii++) {
+ n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
+ assert(n > 0 && n < (int)sizeof(nvlname));
+ nvlist_add_string(nvl, nvlname, hp->h_aliases[ii]);
+ }
+ nvlist_add_number(nvl, "naliases", (uint64_t)ii);
+ }
+
+ if (hp->h_addr_list == NULL) {
+ nvlist_add_number(nvl, "naddrs", 0);
+ } else {
+ for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) {
+ n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
+ assert(n > 0 && n < (int)sizeof(nvlname));
+ nvlist_add_binary(nvl, nvlname, hp->h_addr_list[ii],
+ (size_t)hp->h_length);
+ if (addtocache) {
+ net_add_hostent_to_cache(hp->h_addr_list[ii],
+ hp->h_length, hp->h_addrtype);
+ }
+ }
+ nvlist_add_number(nvl, "naddrs", (uint64_t)ii);
+ }
+}
+
+static int
+net_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin,
+ nvlist_t *nvlout)
+{
+ struct hostent *hp;
+ int family;
+ const nvlist_t *funclimit;
+ const char *name;
+ bool dnscache;
+
+ if (!net_allowed_mode(limits, CAPNET_DEPRECATED_NAME2ADDR))
+ return (ENOTCAPABLE);
+
+ dnscache = net_allowed_mode(limits, CAPNET_CONNECTDNS);
+ funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_NAME2ADDR, NULL);
+
+ family = (int)nvlist_get_number(nvlin, "family");
+ if (!net_allowed_family(funclimit, family))
+ return (ENOTCAPABLE);
+
+ name = nvlist_get_string(nvlin, "name");
+ if (!net_allowed_hosts(funclimit, name, ""))
+ return (ENOTCAPABLE);
+
+ hp = gethostbyname2(name, family);
+ if (hp == NULL)
+ return (h_errno);
+ hostent_pack(hp, nvlout, dnscache);
+ return (0);
+}
+
+static int
+net_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
+ nvlist_t *nvlout)
+{
+ struct hostent *hp;
+ const void *addr;
+ size_t addrsize;
+ int family;
+ const nvlist_t *funclimit;
+
+ if (!net_allowed_mode(limits, CAPNET_DEPRECATED_ADDR2NAME))
+ return (ENOTCAPABLE);
+
+ funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_ADDR2NAME, NULL);
+
+ family = (int)nvlist_get_number(nvlin, "family");
+ if (!net_allowed_family(funclimit, family))
+ return (ENOTCAPABLE);
+
+ addr = nvlist_get_binary(nvlin, "addr", &addrsize);
+ if (!net_allowed_bsaddr(funclimit, addr, addrsize))
+ return (ENOTCAPABLE);
+
+ hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
+ if (hp == NULL)
+ return (h_errno);
+ hostent_pack(hp, nvlout, false);
+ return (0);
+}
+
+static int
+net_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ struct sockaddr_storage sast;
+ const void *sabin;
+ char *host, *serv;
+ size_t sabinsize, hostlen, servlen;
+ socklen_t salen;
+ int error, flags;
+ const nvlist_t *funclimit;
+
+ if (!net_allowed_mode(limits, CAPNET_ADDR2NAME))
+ return (ENOTCAPABLE);
+ funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_ADDR2NAME, NULL);
+
+ error = 0;
+ host = serv = NULL;
+ memset(&sast, 0, sizeof(sast));
+
+ hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
+ servlen = (size_t)nvlist_get_number(nvlin, "servlen");
+
+ if (hostlen > 0) {
+ host = calloc(1, hostlen + 1);
+ if (host == NULL) {
+ error = EAI_MEMORY;
+ goto out;
+ }
+ }
+ if (servlen > 0) {
+ serv = calloc(1, servlen + 1);
+ if (serv == NULL) {
+ error = EAI_MEMORY;
+ goto out;
+ }
+ }
+
+ sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
+ if (sabinsize > sizeof(sast)) {
+ error = EAI_FAIL;
+ goto out;
+ }
+ if (!net_allowed_bsaddr(funclimit, sabin, sabinsize))
+ return (ENOTCAPABLE);
+
+ memcpy(&sast, sabin, sabinsize);
+ salen = (socklen_t)sabinsize;
+
+ if ((sast.ss_family != AF_INET ||
+ salen != sizeof(struct sockaddr_in)) &&
+ (sast.ss_family != AF_INET6 ||
+ salen != sizeof(struct sockaddr_in6))) {
+ error = EAI_FAIL;
+ goto out;
+ }
+
+ if (!net_allowed_family(funclimit, (int)sast.ss_family)) {
+ error = ENOTCAPABLE;
+ goto out;
+ }
+
+ flags = (int)nvlist_get_number(nvlin, "flags");
+
+ error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
+ serv, servlen, flags);
+ if (error != 0)
+ goto out;
+
+ if (host != NULL)
+ nvlist_move_string(nvlout, "host", host);
+ if (serv != NULL)
+ nvlist_move_string(nvlout, "serv", serv);
+out:
+ if (error != 0) {
+ free(host);
+ free(serv);
+ }
+ return (error);
+}
+
+static nvlist_t *
+addrinfo_pack(const struct addrinfo *ai)
+{
+ nvlist_t *nvl;
+
+ nvl = nvlist_create(0);
+ nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
+ nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
+ nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
+ nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
+ nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
+ if (ai->ai_canonname != NULL)
+ nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
+
+ return (nvl);
+}
+
+static int
+net_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ struct addrinfo hints, *hintsp, *res, *cur;
+ const char *hostname, *servname;
+ char nvlname[64];
+ nvlist_t *elem;
+ unsigned int ii;
+ int error, family, n;
+ const nvlist_t *funclimit;
+ bool dnscache;
+
+ if (!net_allowed_mode(limits, CAPNET_NAME2ADDR))
+ return (ENOTCAPABLE);
+ dnscache = net_allowed_mode(limits, CAPNET_CONNECTDNS);
+ funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_NAME2ADDR, NULL);
+
+ hostname = dnvlist_get_string(nvlin, "hostname", NULL);
+ servname = dnvlist_get_string(nvlin, "servname", NULL);
+ if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
+ hints.ai_flags = (int)nvlist_get_number(nvlin,
+ "hints.ai_flags");
+ hints.ai_family = (int)nvlist_get_number(nvlin,
+ "hints.ai_family");
+ hints.ai_socktype = (int)nvlist_get_number(nvlin,
+ "hints.ai_socktype");
+ hints.ai_protocol = (int)nvlist_get_number(nvlin,
+ "hints.ai_protocol");
+ hints.ai_addrlen = 0;
+ hints.ai_addr = NULL;
+ hints.ai_canonname = NULL;
+ hints.ai_next = NULL;
+ hintsp = &hints;
+ family = hints.ai_family;
+ } else {
+ hintsp = NULL;
+ family = AF_UNSPEC;
+ }
+
+ if (!net_allowed_family(funclimit, family))
+ return (ENOTCAPABLE);
+ if (!net_allowed_hosts(funclimit, hostname, servname))
+ return (ENOTCAPABLE);
+ error = getaddrinfo(hostname, servname, hintsp, &res);
+ if (error != 0) {
+ goto out;
+ }
+
+ for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
+ elem = addrinfo_pack(cur);
+ n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
+ assert(n > 0 && n < (int)sizeof(nvlname));
+ nvlist_move_nvlist(nvlout, nvlname, elem);
+ if (dnscache) {
+ net_add_sockaddr_to_cache(cur->ai_addr,
+ cur->ai_addrlen, false);
+ }
+ }
+
+ freeaddrinfo(res);
+ error = 0;
+out:
+ return (error);
+}
+
+static int
+net_bind(const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ int socket, serrno;
+ const void *saddr;
+ size_t len;
+ const nvlist_t *funclimit;
+
+ if (!net_allowed_mode(limits, CAPNET_BIND))
+ return (ENOTCAPABLE);
+ funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_BIND, NULL);
+
+ saddr = nvlist_get_binary(nvlin, "saddr", &len);
+
+ if (!net_allowed_bsaddr(funclimit, saddr, len))
+ return (ENOTCAPABLE);
+
+ socket = nvlist_take_descriptor(nvlin, "s");
+ if (bind(socket, saddr, len) < 0) {
+ serrno = errno;
+ close(socket);
+ return (serrno);
+ }
+
+ nvlist_move_descriptor(nvlout, "s", socket);
+
+ return (0);
+}
+
+static int
+net_connect(const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout)
+{
+ int socket, serrno;
+ const void *saddr;
+ const nvlist_t *funclimit;
+ size_t len;
+ bool conn, conndns;
+
+ conn = net_allowed_mode(limits, CAPNET_CONNECT);
+ conndns = net_allowed_mode(limits, CAPNET_CONNECTDNS);
+
+ if (!conn && !conndns)
+ return (ENOTCAPABLE);
+
+ funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_CONNECT, NULL);
+
+ saddr = nvlist_get_binary(nvlin, "saddr", &len);
+ if (conn && !net_allowed_bsaddr(funclimit, saddr, len)) {
+ return (ENOTCAPABLE);
+ } else if (conndns && (capdnscache == NULL ||
+ !net_allowed_bsaddr_impl(capdnscache, saddr, len))) {
+ return (ENOTCAPABLE);
+ }
+ socket = dup(nvlist_get_descriptor(nvlin, "s"));
+ if (connect(socket, saddr, len) < 0) {
+ serrno = errno;
+ close(socket);
+ return (serrno);
+ }
+
+ nvlist_move_descriptor(nvlout, "s", socket);
+
+ return (0);
+}
+
+static bool
+verify_only_sa_newlimts(const nvlist_t *oldfunclimits,
+ const nvlist_t *newfunclimit)
+{
+ void *cookie;
+
+ cookie = NULL;
+ while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) {
+ void *sacookie;
+
+ if (strcmp(cnvlist_name(cookie), "sockaddr") != 0)
+ return (false);
+
+ if (cnvlist_type(cookie) != NV_TYPE_NVLIST)
+ return (false);
+
+ sacookie = NULL;
+ while (nvlist_next(cnvlist_get_nvlist(cookie), NULL,
+ &sacookie) != NULL) {
+ const void *sa;
+ size_t sasize;
+
+ if (cnvlist_type(sacookie) != NV_TYPE_BINARY)
+ return (false);
+
+ sa = cnvlist_get_binary(sacookie, &sasize);
+ if (!net_allowed_bsaddr(oldfunclimits, sa, sasize))
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+static bool
+verify_bind_newlimts(const nvlist_t *oldlimits,
+ const nvlist_t *newfunclimit)
+{
+ const nvlist_t *oldfunclimits;
+
+ oldfunclimits = NULL;
+ if (oldlimits != NULL) {
+ oldfunclimits = dnvlist_get_nvlist(oldlimits, LIMIT_NV_BIND,
+ NULL);
+ }
+
+ return (verify_only_sa_newlimts(oldfunclimits, newfunclimit));
+}
+
+
+static bool
+verify_connect_newlimits(const nvlist_t *oldlimits,
+ const nvlist_t *newfunclimit)
+{
+ const nvlist_t *oldfunclimits;
+
+ oldfunclimits = NULL;
+ if (oldlimits != NULL) {
+ oldfunclimits = dnvlist_get_nvlist(oldlimits, LIMIT_NV_CONNECT,
+ NULL);
+ }
+
+ return (verify_only_sa_newlimts(oldfunclimits, newfunclimit));
+}
+
+static bool
+verify_addr2name_newlimits(const nvlist_t *oldlimits,
+ const nvlist_t *newfunclimit)
+{
+ void *cookie;
+ const nvlist_t *oldfunclimits;
+
+ oldfunclimits = NULL;
+ if (oldlimits != NULL) {
+ oldfunclimits = dnvlist_get_nvlist(oldlimits,
+ LIMIT_NV_ADDR2NAME, NULL);
+ }
+
+ cookie = NULL;
+ while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) {
+ if (strcmp(cnvlist_name(cookie), "sockaddr") == 0) {
+ void *sacookie;
+
+ if (cnvlist_type(cookie) != NV_TYPE_NVLIST)
+ return (false);
+
+ sacookie = NULL;
+ while (nvlist_next(cnvlist_get_nvlist(cookie), NULL,
+ &sacookie) != NULL) {
+ const void *sa;
+ size_t sasize;
+
+ if (cnvlist_type(sacookie) != NV_TYPE_BINARY)
+ return (false);
+
+ sa = cnvlist_get_binary(sacookie, &sasize);
+ if (!net_allowed_bsaddr(oldfunclimits, sa,
+ sasize)) {
+ return (false);
+ }
+ }
+ } else if (strcmp(cnvlist_name(cookie), "family") == 0) {
+ size_t i, sfamilies;
+ const uint64_t *families;
+
+ if (cnvlist_type(cookie) != NV_TYPE_NUMBER_ARRAY)
+ return (false);
+
+ families = cnvlist_get_number_array(cookie, &sfamilies);
+ for (i = 0; i < sfamilies; i++) {
+ if (!net_allowed_family(oldfunclimits,
+ families[i])) {
+ return (false);
+ }
+ }
+ } else {
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+static bool
+verify_name2addr_newlimits(const nvlist_t *oldlimits,
+ const nvlist_t *newfunclimit)
+{
+ void *cookie;
+ const nvlist_t *oldfunclimits;
+
+ oldfunclimits = NULL;
+ if (oldlimits != NULL) {
+ oldfunclimits = dnvlist_get_nvlist(oldlimits,
+ LIMIT_NV_ADDR2NAME, NULL);
+ }
+
+ cookie = NULL;
+ while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) {
+ if (strcmp(cnvlist_name(cookie), "hosts") == 0) {
+ void *hostcookie;
+
+ if (cnvlist_type(cookie) != NV_TYPE_NVLIST)
+ return (false);
+
+ hostcookie = NULL;
+ while (nvlist_next(cnvlist_get_nvlist(cookie), NULL,
+ &hostcookie) != NULL) {
+ if (cnvlist_type(hostcookie) != NV_TYPE_STRING)
+ return (false);
+
+ if (!net_allowed_hosts(oldfunclimits,
+ cnvlist_name(hostcookie),
+ cnvlist_get_string(hostcookie))) {
+ return (false);
+ }
+ }
+ } else if (strcmp(cnvlist_name(cookie), "family") == 0) {
+ size_t i, sfamilies;
+ const uint64_t *families;
+
+ if (cnvlist_type(cookie) != NV_TYPE_NUMBER_ARRAY)
+ return (false);
+
+ families = cnvlist_get_number_array(cookie, &sfamilies);
+ for (i = 0; i < sfamilies; i++) {
+ if (!net_allowed_family(oldfunclimits,
+ families[i])) {
+ return (false);
+ }
+ }
+ } else {
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+static int
+net_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
+{
+ const char *name;
+ void *cookie;
+ bool hasmode, hasconnect, hasbind, hasaddr2name, hasname2addr;
+
+ /*
+ * Modes:
+ * ADDR2NAME:
+ * getnameinfo
+ * DEPRECATED_ADDR2NAME:
+ * gethostbyaddr
+ *
+ * NAME2ADDR:
+ * getaddrinfo
+ * DEPRECATED_NAME2ADDR:
+ * gethostbyname
+ *
+ * Limit scheme:
+ * mode : NV_TYPE_NUMBER
+ * connect : NV_TYPE_NVLIST
+ * sockaddr : NV_TYPE_NVLIST
+ * "" : NV_TYPE_BINARY
+ * ... : NV_TYPE_BINARY
+ * bind : NV_TYPE_NVLIST
+ * sockaddr : NV_TYPE_NVLIST
+ * "" : NV_TYPE_BINARY
+ * ... : NV_TYPE_BINARY
+ * addr2name : NV_TYPE_NVLIST
+ * family : NV_TYPE_NUMBER_ARRAY
+ * sockaddr : NV_TYPE_NVLIST
+ * "" : NV_TYPE_BINARY
+ * ... : NV_TYPE_BINARY
+ * name2addr : NV_TYPE_NVLIST
+ * family : NV_TYPE_NUMBER
+ * hosts : NV_TYPE_NVLIST
+ * host : servname : NV_TYPE_STRING
+ */
+
+ hasmode = false;
+ hasconnect = false;
+ hasbind = false;
+ hasaddr2name = false;
+ hasname2addr = false;
+
+ cookie = NULL;
+ while ((name = nvlist_next(newlimits, NULL, &cookie)) != NULL) {
+ if (strcmp(name, "mode") == 0) {
+ if (cnvlist_type(cookie) != NV_TYPE_NUMBER) {
+ return (NO_RECOVERY);
+ }
+ if (!net_allowed_mode(oldlimits,
+ cnvlist_get_number(cookie))) {
+ return (ENOTCAPABLE);
+ }
+ hasmode = true;
+ continue;
+ }
+
+ if (cnvlist_type(cookie) != NV_TYPE_NVLIST) {
+ return (NO_RECOVERY);
+ }
+
+ if (strcmp(name, LIMIT_NV_BIND) == 0) {
+ hasbind = true;
+ if (!verify_bind_newlimts(oldlimits,
+ cnvlist_get_nvlist(cookie))) {
+ return (ENOTCAPABLE);
+ }
+ } else if (strcmp(name, LIMIT_NV_CONNECT) == 0) {
+ hasconnect = true;
+ if (!verify_connect_newlimits(oldlimits,
+ cnvlist_get_nvlist(cookie))) {
+ return (ENOTCAPABLE);
+ }
+ } else if (strcmp(name, LIMIT_NV_ADDR2NAME) == 0) {
+ hasaddr2name = true;
+ if (!verify_addr2name_newlimits(oldlimits,
+ cnvlist_get_nvlist(cookie))) {
+ return (ENOTCAPABLE);
+ }
+ } else if (strcmp(name, LIMIT_NV_NAME2ADDR) == 0) {
+ hasname2addr = true;
+ if (!verify_name2addr_newlimits(oldlimits,
+ cnvlist_get_nvlist(cookie))) {
+ return (ENOTCAPABLE);
+ }
+ }
+ }
+
+ /* Mode is required. */
+ if (!hasmode)
+ return (ENOTCAPABLE);
+
+ /*
+ * If the new limit doesn't mention mode or family we have to
+ * check if the current limit does have those. Missing mode or
+ * family in the limit means that all modes or families are
+ * allowed.
+ */
+ if (oldlimits == NULL)
+ return (0);
+ if (!hasconnect && nvlist_exists(oldlimits, LIMIT_NV_BIND))
+ return (ENOTCAPABLE);
+ if (!hasconnect && nvlist_exists(oldlimits, LIMIT_NV_CONNECT))
+ return (ENOTCAPABLE);
+ if (!hasaddr2name && nvlist_exists(oldlimits, LIMIT_NV_ADDR2NAME))
+ return (ENOTCAPABLE);
+ if (!hasname2addr && nvlist_exists(oldlimits, LIMIT_NV_NAME2ADDR))
+ return (ENOTCAPABLE);
+ return (0);
+}
+
+static int
+net_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
+ nvlist_t *nvlout)
+{
+
+ if (strcmp(cmd, "bind") == 0)
+ return (net_bind(limits, nvlin, nvlout));
+ else if (strcmp(cmd, "connect") == 0)
+ return (net_connect(limits, nvlin, nvlout));
+ else if (strcmp(cmd, "gethostbyname") == 0)
+ return (net_gethostbyname(limits, nvlin, nvlout));
+ else if (strcmp(cmd, "gethostbyaddr") == 0)
+ return (net_gethostbyaddr(limits, nvlin, nvlout));
+ else if (strcmp(cmd, "getnameinfo") == 0)
+ return (net_getnameinfo(limits, nvlin, nvlout));
+ else if (strcmp(cmd, "getaddrinfo") == 0)
+ return (net_getaddrinfo(limits, nvlin, nvlout));
+
+ return (EINVAL);
+}
+
+CREATE_SERVICE("system.net", net_limit, net_command, 0);
Index: head/lib/libcasper/services/cap_net/tests/Makefile
===================================================================
--- head/lib/libcasper/services/cap_net/tests/Makefile
+++ head/lib/libcasper/services/cap_net/tests/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+.include <src.opts.mk>
+
+ATF_TESTS_C= net_test
+
+.if ${MK_CASPER} != "no"
+LIBADD+= casper
+LIBADD+= cap_net
+CFLAGS+=-DWITH_CASPER
+.endif
+LIBADD+= nv
+
+WARNS?= 3
+
+.include <bsd.test.mk>
Index: head/lib/libcasper/services/cap_net/tests/net_test.c
===================================================================
--- head/lib/libcasper/services/cap_net/tests/net_test.c
+++ head/lib/libcasper/services/cap_net/tests/net_test.c
@@ -0,0 +1,1160 @@
+/*-
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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"
+
+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 ret;
+
+ memset(&ipaddr, 0, sizeof(ipaddr));
+ ipaddr.sin_family = family;
+ inet_pton(family, ip, &ipaddr.sin_addr);
+
+ ret = cap_getnameinfo(chan, (struct sockaddr *)&ipaddr, sizeof(ipaddr),
+ capfn, sizeof(capfn), NULL, 0, NI_NAMEREQD);
+ if (ret != 0) {
+ return (ret);
+ }
+
+ ret = getnameinfo((struct sockaddr *)&ipaddr, sizeof(ipaddr), origfn,
+ sizeof(origfn), NULL, 0, NI_NAMEREQD);
+ ATF_REQUIRE(ret == 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 ret;
+
+ memset(&ipaddr, 0, sizeof(ipaddr));
+ ipaddr.sin6_family = AF_INET6;
+ inet_pton(AF_INET6, ip, &ipaddr.sin6_addr);
+
+ ret = cap_getnameinfo(chan, (struct sockaddr *)&ipaddr, sizeof(ipaddr),
+ capfn, sizeof(capfn), NULL, 0, NI_NAMEREQD);
+ if (ret != 0) {
+ return (ret);
+ }
+
+ ret = getnameinfo((struct sockaddr *)&ipaddr, sizeof(ipaddr), origfn,
+ sizeof(origfn), NULL, 0, NI_NAMEREQD);
+ ATF_REQUIRE(ret == 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) {
+ return (h_errno);
+ }
+
+ orighp = gethostbyaddr(&ipaddr, sizeof(ipaddr), family);
+ ATF_REQUIRE(orighp != NULL);
+ ATF_REQUIRE(strcmp(caphp->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)
+ return (h_errno);
+
+ orighp = gethostbyaddr(&ipaddr, sizeof(ipaddr), AF_INET6);
+ ATF_REQUIRE(orighp != NULL);
+ ATF_REQUIRE(strcmp(caphp->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 ret;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+
+ ret = cap_getaddrinfo(chan, domain, servname, &hints, &capres);
+ if (ret != 0) {
+ return (ret);
+ }
+
+ ret = getaddrinfo(domain, servname, &hints, &origres);
+ ATF_REQUIRE(ret == 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) {
+ return (h_errno);
+ }
+
+ orighp = gethostbyname2(domain, family);
+ ATF_REQUIRE(orighp != 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;
+ close(capfd);
+
+ 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_WITHOUT_HEAD(capnet__limits_addr2name_mode);
+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_WITHOUT_HEAD(capnet__limits_addr2name_family);
+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_WITHOUT_HEAD(capnet__limits_addr2name);
+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_WITHOUT_HEAD(capnet__limits_deprecated_addr2name_mode);
+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_WITHOUT_HEAD(capnet__limits_deprecated_addr2name_family);
+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_WITHOUT_HEAD(capnet__limits_deprecated_addr2name);
+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_WITHOUT_HEAD(capnet__limits_name2addr_mode);
+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_WITHOUT_HEAD(capnet__limits_name2addr_hosts);
+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);
+
+ cap_close(capnet);
+}
+
+ATF_TC_WITHOUT_HEAD(capnet__limits_name2addr_hosts_servnames_strict);
+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_WITHOUT_HEAD(capnet__limits_name2addr_hosts_servnames_mix);
+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 HTTTP 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_WITHOUT_HEAD(capnet__limits_name2addr_family);
+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_WITHOUT_HEAD(capnet__limits_deprecated_name2addr_mode);
+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_WITHOUT_HEAD(capnet__limits_deprecated_name2addr_hosts);
+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_WITHOUT_HEAD(capnet__limits_deprecated_name2addr_family);
+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_WITHOUT_HEAD(capnet__limits_bind_mode);
+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_WITHOUT_HEAD(capnet__limits_bind);
+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_WITHOUT_HEAD(capnet__limits_connect_mode);
+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_WITHOUT_HEAD(capnet__limits_connect);
+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_WITHOUT_HEAD(capnet__limits_connecttodns);
+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 };
+
+ 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);
+
+ ATF_REQUIRE(cap_connect(capnet, s, res->ai_addr,
+ res->ai_addrlen) == 0);
+ close(s);
+ }
+
+ freeaddrinfo(capres);
+ cap_close(capnet);
+}
+
+
+ATF_TC_WITHOUT_HEAD(capnet__limits_deprecated_connecttodns);
+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 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);
+
+ ATF_REQUIRE(cap_connect(capnet, s, (struct sockaddr *)&connaddr,
+ sizeof(connaddr)) == 0);
+ close(s);
+ }
+
+ cap_close(capnet);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ 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);
+
+ ATF_TP_ADD_TC(tp, capnet__limits_connecttodns);
+ ATF_TP_ADD_TC(tp, capnet__limits_deprecated_connecttodns);
+
+ return (atf_no_error());
+}
Index: head/share/mk/src.libnames.mk
===================================================================
--- head/share/mk/src.libnames.mk
+++ head/share/mk/src.libnames.mk
@@ -89,6 +89,7 @@
cap_dns \
cap_fileargs \
cap_grp \
+ cap_net \
cap_pwd \
cap_sysctl \
cap_syslog \
@@ -637,6 +638,7 @@
LIBCASPERDIR= ${OBJTOP}/lib/libcasper/libcasper
LIBCAP_DNSDIR= ${OBJTOP}/lib/libcasper/services/cap_dns
LIBCAP_GRPDIR= ${OBJTOP}/lib/libcasper/services/cap_grp
+LIBCAP_NETDIR= ${OBJTOP}/lib/libcasper/services/cap_net
LIBCAP_PWDDIR= ${OBJTOP}/lib/libcasper/services/cap_pwd
LIBCAP_SYSCTLDIR= ${OBJTOP}/lib/libcasper/services/cap_sysctl
LIBCAP_SYSLOGDIR= ${OBJTOP}/lib/libcasper/services/cap_syslog
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Jan 31, 9:55 PM (20 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16370773
Default Alt Text
D24688.diff (89 KB)
Attached To
Mode
D24688: Introduce cap_net a network service for Casper.
Attached
Detach File
Event Timeline
Log In to Comment