svn commit: r364276 - in head: lib/libcasper/services lib/libcasper/services/cap_dns lib/libcasper/services/cap_net lib/libcasper/services/cap_net/tests share/mk

Mariusz Zaborski oshogbo at FreeBSD.org
Sun Aug 16 18:12:23 UTC 2020


Author: oshogbo
Date: Sun Aug 16 18:12:21 2020
New Revision: 364276
URL: https://svnweb.freebsd.org/changeset/base/364276

Log:
  libcasper: Introduce cap_net a network service for Casper.
  
  Reviewed by:	emaste, markj (previous version), bcr (man page)
  Differential Revision:	https://reviews.freebsd.org/D24688

Added:
  head/lib/libcasper/services/cap_net/
  head/lib/libcasper/services/cap_net/Makefile   (contents, props changed)
  head/lib/libcasper/services/cap_net/cap_net.3   (contents, props changed)
  head/lib/libcasper/services/cap_net/cap_net.c   (contents, props changed)
  head/lib/libcasper/services/cap_net/cap_net.h   (contents, props changed)
  head/lib/libcasper/services/cap_net/tests/
  head/lib/libcasper/services/cap_net/tests/Makefile   (contents, props changed)
  head/lib/libcasper/services/cap_net/tests/net_test.c   (contents, props changed)
Modified:
  head/lib/libcasper/services/Makefile
  head/lib/libcasper/services/cap_dns/Makefile
  head/lib/libcasper/services/cap_dns/cap_dns.3
  head/share/mk/src.libnames.mk

Modified: head/lib/libcasper/services/Makefile
==============================================================================
--- head/lib/libcasper/services/Makefile	Sun Aug 16 18:10:15 2020	(r364275)
+++ head/lib/libcasper/services/Makefile	Sun Aug 16 18:12:21 2020	(r364276)
@@ -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

Modified: head/lib/libcasper/services/cap_dns/Makefile
==============================================================================
--- head/lib/libcasper/services/cap_dns/Makefile	Sun Aug 16 18:10:15 2020	(r364275)
+++ head/lib/libcasper/services/cap_dns/Makefile	Sun Aug 16 18:12:21 2020	(r364276)
@@ -27,11 +27,6 @@ SUBDIR.${MK_TESTS}+= tests
 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
 

Modified: head/lib/libcasper/services/cap_dns/cap_dns.3
==============================================================================
--- head/lib/libcasper/services/cap_dns/cap_dns.3	Sun Aug 16 18:10:15 2020	(r364275)
+++ head/lib/libcasper/services/cap_dns/cap_dns.3	Sun Aug 16 18:12:21 2020	(r364276)
@@ -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

Added: head/lib/libcasper/services/cap_net/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libcasper/services/cap_net/Makefile	Sun Aug 16 18:12:21 2020	(r364276)
@@ -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>

Added: head/lib/libcasper/services/cap_net/cap_net.3
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libcasper/services/cap_net/cap_net.3	Sun Aug 16 18:12:21 2020	(r364276)
@@ -0,0 +1,287 @@
+.\" Copyright (c) 2020 Mariusz Zaborski <oshogbo at 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 at FreeBSD.org

Added: head/lib/libcasper/services/cap_net/cap_net.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/lib/libcasper/services/cap_net/cap_net.c	Sun Aug 16 18:12:21 2020	(r364276)
@@ -0,0 +1,1385 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2020 Mariusz Zaborski <oshogbo at 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);
+}

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list