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 <osho...@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 osho...@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 <osho...@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 ***
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to