Index: head/lib/libcasper/services/Makefile =================================================================== --- head/lib/libcasper/services/Makefile (revision 364275) +++ head/lib/libcasper/services/Makefile (revision 364276) @@ -1,16 +1,17 @@ # $FreeBSD$ .include SUBDIR= cap_dns SUBDIR+= cap_fileargs SUBDIR+= cap_grp +SUBDIR+= cap_net SUBDIR+= cap_pwd SUBDIR+= cap_sysctl SUBDIR+= cap_syslog SUBDIR.${MK_TESTS}+= tests SUBDIR_PARALLEL= .include Index: head/lib/libcasper/services/cap_dns/Makefile =================================================================== --- head/lib/libcasper/services/cap_dns/Makefile (revision 364275) +++ head/lib/libcasper/services/cap_dns/Makefile (revision 364276) @@ -1,38 +1,33 @@ # $FreeBSD$ SHLIBDIR?= /lib/casper .include PACKAGE= runtime SHLIB_MAJOR= 2 INCSDIR?= ${INCLUDEDIR}/casper .if ${MK_CASPER} != "no" SHLIB= cap_dns SRCS= cap_dns.c .endif INCS= cap_dns.h LIBADD= nv CFLAGS+=-I${.CURDIR} HAS_TESTS= 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 .include Index: head/lib/libcasper/services/cap_dns/cap_dns.3 =================================================================== --- head/lib/libcasper/services/cap_dns/cap_dns.3 (revision 364275) +++ head/lib/libcasper/services/cap_dns/cap_dns.3 (revision 364276) @@ -1,239 +1,242 @@ .\" Copyright (c) 2018 Mariusz Zaborski .\" All rights reserved. .\" .\" 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 May 5, 2020 +.Dd August 15, 2020 .Dt CAP_DNS 3 .Os .Sh NAME .Nm cap_getaddrinfo , .Nm cap_getnameinfo , .Nm cap_gethostbyname , .Nm cap_gethostbyname2 , .Nm cap_gethostbyaddr , .Nm cap_dns_type_limit , .Nm cap_dns_family_limit .Nd "library for getting network host entry in capability mode" .Sh LIBRARY .Lb libcap_dns .Sh SYNOPSIS .In sys/nv.h .In libcasper.h .In casper/cap_dns.h .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 "int" .Fn cap_dns_type_limit "cap_channel_t *chan" "const char * const *types" "size_t ntypes" .Ft "int" .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 .Fn cap_getnameinfo , functions are preferred over the .Fn cap_gethostbyname , .Fn cap_gethostbyname2 , and .Fn cap_gethostbyaddr functions. .Ef .Pp The functions .Fn cap_gethostbyname , .Fn cap_gethostbyname2 , .Fn cep_gethostbyaddr and .Fn cap_getnameinfo are respectively equivalent to .Xr gethostbyname 3 , .Xr gethostbyname2 3 , .Xr gethostbyaddr 3 and .Xr getnameinfo 3 except that the connection to the .Nm system.dns service needs to be provided. .Pp The .Fn cap_dns_type_limit function limits the functions allowed in the service. The .Fa types variable can be set to .Dv ADDR2NAME or .Dv NAME2ADDR . See the .Sx LIMITS section for more details. The .Fa ntpyes variable contains the number of .Fa types provided. .Pp The .Fn cap_dns_family_limit functions allows to limit address families. For details see .Sx LIMITS . The .Fa nfamilies variable contains the number of .Fa families provided. .Sh LIMITS The preferred way of setting limits is to use the .Fn cap_dns_type_limit and .Fn cap_dns_family_limit functions, but the limits of service can be set also using .Xr cap_limit_set 3 . The .Xr nvlist 9 for that function can contain the following values and types: .Bl -ohang -offset indent .It type ( NV_TYPE_STRING ) The .Va type can have two values: .Dv ADDR2NAME or .Dv NAME2ADDR . The .Dv ADDR2NAME means that reverse DNS lookups are allowed with .Fn cap_getnameinfo and .Fn cap_gethostbyaddr functions. In case when .Va type is set to .Dv NAME2ADDR the name resolution is allowed with .Fn cap_getaddrinfo , .Fn cap_gethostbyname , and .Fn cap_gethostbyname2 functions. .It family ( NV_TYPE_NUMBER ) The .Va family limits service to one of the address families (e.g. .Dv AF_INET , AF_INET6 , etc.). .Sh EXAMPLES The following example first opens a capability to casper and then uses this capability to create the .Nm system.dns casper service and uses it to resolve an IP address. .Bd -literal cap_channel_t *capcas, *capdns; int familylimit, error; const char *ipstr = "127.0.0.1"; const char *typelimit = "ADDR2NAME"; char hname[NI_MAXHOST]; 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() < 0) err(1, "Unable to enter capability mode"); /* Use Casper capability to create capability to the system.dns service. */ capdns = cap_service_open(capcas, "system.dns"); if (capdns == NULL) err(1, "Unable to open system.dns service"); /* Close Casper capability, we don't need it anymore. */ cap_close(capcas); /* Limit system.dns to reserve IPv4 addresses */ familylimit = AF_INET; if (cap_dns_family_limit(capdns, &familylimit, 1) < 0) err(1, "Unable to limit access to the system.dns service"); /* Convert IP address in C-string to struct sockaddr. */ memset(&hints, 0, sizeof(hints)); hints.ai_family = familylimit; hints.ai_flags = AI_NUMERICHOST; error = cap_getaddrinfo(capdns, ipstr, NULL, &hints, &res); if (error != 0) errx(1, "cap_getaddrinfo(): %s: %s", ipstr, gai_strerror(error)); /* Limit system.dns to reverse DNS lookups. */ if (cap_dns_type_limit(capdns, &typelimit, 1) < 0) err(1, "Unable to limit access to the system.dns service"); /* Find hostname for the given IP address. */ error = cap_getnameinfo(capdns, res->ai_addr, res->ai_addrlen, hname, sizeof(hname), NULL, 0, 0); if (error != 0) errx(1, "cap_getnameinfo(): %s: %s", ipstr, gai_strerror(error)); printf("Name associated with %s is %s.\\n", ipstr, hname); .Ed .Sh SEE ALSO .Xr cap_enter 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 HISTORY The .Nm cap_dns service first appeared in .Fx 10.3 . .Sh AUTHORS The .Nm cap_dns service was implemented by .An Pawel Jakub Dawidek Aq Mt pawel@dawidek.net under sponsorship from the FreeBSD Foundation. .Pp This manual page was written by .An Mariusz Zaborski Aq Mt oshogbo@FreeBSD.org . Index: head/lib/libcasper/services/cap_net/Makefile =================================================================== --- head/lib/libcasper/services/cap_net/Makefile (nonexistent) +++ head/lib/libcasper/services/cap_net/Makefile (revision 364276) @@ -0,0 +1,48 @@ +# $FreeBSD$ + +SHLIBDIR?= /lib/casper + +.include + +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 Property changes on: head/lib/libcasper/services/cap_net/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/lib/libcasper/services/cap_net/cap_net.3 =================================================================== --- head/lib/libcasper/services/cap_net/cap_net.3 (nonexistent) +++ head/lib/libcasper/services/cap_net/cap_net.3 (revision 364276) @@ -0,0 +1,287 @@ +.\" Copyright (c) 2020 Mariusz Zaborski +.\" +.\" 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 Property changes on: head/lib/libcasper/services/cap_net/cap_net.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/lib/libcasper/services/cap_net/cap_net.c =================================================================== --- head/lib/libcasper/services/cap_net/cap_net.c (nonexistent) +++ head/lib/libcasper/services/cap_net/cap_net.c (revision 364276) @@ -0,0 +1,1385 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Mariusz Zaborski + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#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); Property changes on: head/lib/libcasper/services/cap_net/cap_net.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/lib/libcasper/services/cap_net/cap_net.h =================================================================== --- head/lib/libcasper/services/cap_net/cap_net.h (nonexistent) +++ head/lib/libcasper/services/cap_net/cap_net.h (revision 364276) @@ -0,0 +1,165 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 Mariusz Zaborski + * + * 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 +#include + +#include + +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_ */ Property changes on: head/lib/libcasper/services/cap_net/cap_net.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/lib/libcasper/services/cap_net/tests/Makefile =================================================================== --- head/lib/libcasper/services/cap_net/tests/Makefile (nonexistent) +++ head/lib/libcasper/services/cap_net/tests/Makefile (revision 364276) @@ -0,0 +1,16 @@ +# $FreeBSD$ + +.include + +ATF_TESTS_C= net_test + +.if ${MK_CASPER} != "no" +LIBADD+= casper +LIBADD+= cap_net +CFLAGS+=-DWITH_CASPER +.endif +LIBADD+= nv + +WARNS?= 3 + +.include Property changes on: head/lib/libcasper/services/cap_net/tests/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/lib/libcasper/services/cap_net/tests/net_test.c =================================================================== --- head/lib/libcasper/services/cap_net/tests/net_test.c (nonexistent) +++ head/lib/libcasper/services/cap_net/tests/net_test.c (revision 364276) @@ -0,0 +1,1160 @@ +/*- + * Copyright (c) 2020 Mariusz Zaborski + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#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()); +} Property changes on: head/lib/libcasper/services/cap_net/tests/net_test.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/share/mk/src.libnames.mk =================================================================== --- head/share/mk/src.libnames.mk (revision 364275) +++ head/share/mk/src.libnames.mk (revision 364276) @@ -1,696 +1,698 @@ # $FreeBSD$ # # The include file define library names suitable # for INTERNALLIB and PRIVATELIB definition .if !target(____) .error src.libnames.mk cannot be included directly. .endif .if !target(____) ____: .include _PRIVATELIBS= \ atf_c \ atf_cxx \ auditd \ bsdstat \ devdctl \ event1 \ gmock \ gtest \ gmock_main \ gtest_main \ heimipcc \ heimipcs \ ldns \ sqlite3 \ ssh \ ucl \ unbound \ zstd _INTERNALLIBS= \ amu \ bsnmptools \ c_nossp_pic \ cron \ elftc \ fifolog \ ifconfig \ ipf \ kyua_cli \ kyua_drivers \ kyua_engine \ kyua_model \ kyua_store \ kyua_utils \ lpr \ lua \ lutok \ netbsd \ ntp \ ntpevent \ openbsd \ opts \ parse \ pe \ pmcstat \ sl \ sm \ smdb \ smutil \ telnet \ vers _LIBRARIES= \ ${_PRIVATELIBS} \ ${_INTERNALLIBS} \ ${LOCAL_LIBRARIES} \ 80211 \ alias \ archive \ asn1 \ avl \ be \ begemot \ bluetooth \ bsdxml \ bsm \ bsnmp \ bz2 \ c \ c_pic \ calendar \ cam \ casper \ cap_dns \ cap_fileargs \ cap_grp \ + cap_net \ cap_pwd \ cap_sysctl \ cap_syslog \ com_err \ compiler_rt \ crypt \ crypto \ ctf \ cuse \ cxxrt \ devctl \ devdctl \ devinfo \ devstat \ dialog \ dl \ dpv \ dtrace \ dwarf \ edit \ efivar \ elf \ execinfo \ fetch \ figpar \ geom \ gnuregex \ gpio \ gssapi \ gssapi_krb5 \ hdb \ heimbase \ heimntlm \ heimsqlite \ hx509 \ ipsec \ ipt \ jail \ kadm5clnt \ kadm5srv \ kafs5 \ kdc \ kiconv \ krb5 \ kvm \ l \ lzma \ m \ magic \ md \ memstat \ mp \ mt \ ncurses \ ncursesw \ netgraph \ ngatm \ nv \ nvpair \ opencsd \ opie \ pam \ panel \ panelw \ pcap \ pcsclite \ pjdlog \ pmc \ proc \ procstat \ pthread \ radius \ regex \ roken \ rpcsec_gss \ rpcsvc \ rt \ rtld_db \ sbuf \ sdp \ sm \ smb \ ssl \ ssp_nonshared \ stats \ stdthreads \ supcplusplus \ sysdecode \ tacplus \ termcap \ termcapw \ ufs \ ugidfw \ ulog \ umem \ usb \ usbhid \ util \ uutil \ vmmapi \ wind \ wrap \ xo \ y \ ypclnt \ z \ zfs_core \ zfs \ zpool \ .if ${MK_BLACKLIST} != "no" _LIBRARIES+= \ blacklist \ .endif .if ${MK_OFED} != "no" _LIBRARIES+= \ cxgb4 \ ibcm \ ibmad \ ibnetdisc \ ibumad \ ibverbs \ mlx4 \ mlx5 \ rdmacm \ osmcomp \ opensm \ osmvendor .endif .if ${MK_BEARSSL} == "yes" _LIBRARIES+= \ bearssl \ secureboot \ LIBBEARSSL?= ${LIBBEARSSLDIR}/libbearssl.a LIBSECUREBOOT?= ${LIBSECUREBOOTDIR}/libsecureboot.a .endif .if ${MK_VERIEXEC} == "yes" _LIBRARIES+= veriexec LIBVERIEXEC?= ${LIBVERIEXECDIR}/libveriexec.a .endif # Each library's LIBADD needs to be duplicated here for static linkage of # 2nd+ order consumers. Auto-generating this would be better. _DP_80211= sbuf bsdxml _DP_archive= z bz2 lzma bsdxml zstd _DP_zstd= pthread .if ${MK_BLACKLIST} != "no" _DP_blacklist+= pthread .endif _DP_crypto= pthread .if ${MK_OPENSSL} != "no" _DP_archive+= crypto .else _DP_archive+= md .endif _DP_sqlite3= pthread _DP_ssl= crypto _DP_ssh= crypto crypt z .if ${MK_LDNS} != "no" _DP_ssh+= ldns .endif _DP_edit= ncursesw .if ${MK_OPENSSL} != "no" _DP_bsnmp= crypto .endif _DP_geom= bsdxml sbuf _DP_cam= sbuf _DP_kvm= elf _DP_kyua_cli= kyua_drivers kyua_engine kyua_model kyua_store kyua_utils _DP_kyua_drivers= kyua_model kyua_engine kyua_store _DP_kyua_engine= lutok kyua_utils _DP_kyua_model= lutok _DP_kyua_utils= lutok _DP_kyua_store= kyua_model kyua_utils sqlite3 _DP_casper= nv _DP_cap_dns= nv _DP_cap_fileargs= nv _DP_cap_grp= nv _DP_cap_pwd= nv _DP_cap_sysctl= nv _DP_cap_syslog= nv .if ${MK_OFED} != "no" _DP_pcap= ibverbs mlx5 .endif _DP_pjdlog= util _DP_opie= md _DP_usb= pthread _DP_unbound= ssl crypto pthread _DP_rt= pthread .if ${MK_OPENSSL} == "no" _DP_radius= md .else _DP_radius= crypto .endif _DP_rtld_db= elf procstat _DP_procstat= kvm util elf .if ${MK_CXX} == "yes" .if ${MK_LIBCPLUSPLUS} != "no" _DP_proc= cxxrt .else _DP_proc= supcplusplus .endif .endif .if ${MK_CDDL} != "no" _DP_proc+= ctf .endif _DP_proc+= elf procstat rtld_db util _DP_mp= crypto _DP_memstat= kvm _DP_magic= z _DP_mt= sbuf bsdxml _DP_ldns= ssl crypto _DP_lua= m _DP_lutok= lua .if ${MK_OPENSSL} != "no" _DP_fetch= ssl crypto .else _DP_fetch= md .endif _DP_execinfo= elf _DP_dwarf= elf _DP_dpv= dialog figpar util ncursesw _DP_dialog= ncursesw m _DP_cuse= pthread _DP_atf_cxx= atf_c _DP_gtest= pthread regex _DP_gmock= gtest _DP_gmock_main= gmock _DP_gtest_main= gtest _DP_devstat= kvm _DP_pam= radius tacplus opie md util .if ${MK_KERBEROS} != "no" _DP_pam+= krb5 .endif .if ${MK_OPENSSH} != "no" _DP_pam+= ssh .endif .if ${MK_NIS} != "no" _DP_pam+= ypclnt .endif _DP_roken= crypt _DP_kadm5clnt= com_err krb5 roken _DP_kadm5srv= com_err hdb krb5 roken _DP_heimntlm= crypto com_err krb5 roken _DP_hx509= asn1 com_err crypto roken wind _DP_hdb= asn1 com_err krb5 roken sqlite3 _DP_asn1= com_err roken _DP_kdc= roken hdb hx509 krb5 heimntlm asn1 crypto _DP_wind= com_err roken _DP_heimbase= pthread _DP_heimipcc= heimbase roken pthread _DP_heimipcs= heimbase roken pthread _DP_kafs5= asn1 krb5 roken _DP_krb5+= asn1 com_err crypt crypto hx509 roken wind heimbase heimipcc _DP_gssapi_krb5+= gssapi krb5 crypto roken asn1 com_err _DP_lzma= md pthread _DP_ucl= m _DP_vmmapi= util _DP_opencsd= cxxrt _DP_ctf= z _DP_dtrace= ctf elf proc pthread rtld_db _DP_xo= util # The libc dependencies are not strictly needed but are defined to make the # assert happy. _DP_c= compiler_rt .if ${MK_SSP} != "no" && \ (${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH:Mpower*} != "") _DP_c+= ssp_nonshared .endif _DP_stats= sbuf pthread _DP_stdthreads= pthread _DP_tacplus= md _DP_panel= ncurses _DP_panelw= ncursesw _DP_rpcsec_gss= gssapi _DP_smb= kiconv _DP_ulog= md _DP_fifolog= z _DP_ipf= kvm _DP_zfs= md pthread umem util uutil m nvpair avl bsdxml geom nvpair z \ zfs_core _DP_zfs_core= nvpair _DP_zpool= md pthread z nvpair avl umem _DP_be= zfs nvpair # OFED support .if ${MK_OFED} != "no" _DP_cxgb4= ibverbs pthread _DP_ibcm= ibverbs _DP_ibmad= ibumad _DP_ibnetdisc= osmcomp ibmad ibumad _DP_ibumad= _DP_ibverbs= _DP_mlx4= ibverbs pthread _DP_mlx5= ibverbs pthread _DP_rdmacm= ibverbs _DP_osmcomp= pthread _DP_opensm= pthread _DP_osmvendor= ibumad pthread .endif # Define special cases LDADD_supcplusplus= -lsupc++ LIBATF_C= ${LIBDESTDIR}${LIBDIR_BASE}/libprivateatf-c.a LIBATF_CXX= ${LIBDESTDIR}${LIBDIR_BASE}/libprivateatf-c++.a LDADD_atf_c= -lprivateatf-c LDADD_atf_cxx= -lprivateatf-c++ LIBGMOCK= ${LIBDESTDIR}${LIBDIR_BASE}/libprivategmock.a LIBGMOCK_MAIN= ${LIBDESTDIR}${LIBDIR_BASE}/libprivategmock_main.a LIBGTEST= ${LIBDESTDIR}${LIBDIR_BASE}/libprivategtest.a LIBGTEST_MAIN= ${LIBDESTDIR}${LIBDIR_BASE}/libprivategtest_main.a LDADD_gmock= -lprivategmock LDADD_gtest= -lprivategtest LDADD_gmock_main= -lprivategmock_main LDADD_gtest_main= -lprivategtest_main .for _l in ${_PRIVATELIBS} LIB${_l:tu}?= ${LIBDESTDIR}${LIBDIR_BASE}/libprivate${_l}.a .endfor .if ${MK_PIE} != "no" PIE_SUFFIX= _pie .endif .for _l in ${_LIBRARIES} .if ${_INTERNALLIBS:M${_l}} || !defined(SYSROOT) LDADD_${_l}_L+= -L${LIB${_l:tu}DIR} .endif DPADD_${_l}?= ${LIB${_l:tu}} .if ${_PRIVATELIBS:M${_l}} LDADD_${_l}?= -lprivate${_l} .elif ${_INTERNALLIBS:M${_l}} LDADD_${_l}?= ${LDADD_${_l}_L} -l${_l:S/${PIE_SUFFIX}//}${PIE_SUFFIX} .else LDADD_${_l}?= ${LDADD_${_l}_L} -l${_l} .endif # Add in all dependencies for static linkage. .if defined(_DP_${_l}) && (${_INTERNALLIBS:M${_l}} || \ (defined(NO_SHARED) && ${NO_SHARED:tl} != "no")) .for _d in ${_DP_${_l}} DPADD_${_l}+= ${DPADD_${_d}} LDADD_${_l}+= ${LDADD_${_d}} .endfor .endif .endfor # These are special cases where the library is broken and anything that uses # it needs to add more dependencies. Broken usually means that it has a # cyclic dependency and cannot link its own dependencies. This is bad, please # fix the library instead. # Unless the library itself is broken then the proper place to define # dependencies is _DP_* above. # libatf-c++ exposes libatf-c abi hence we need to explicit link to atf_c for # atf_cxx DPADD_atf_cxx+= ${DPADD_atf_c} LDADD_atf_cxx+= ${LDADD_atf_c} DPADD_gmock+= ${DPADD_gtest} LDADD_gmock+= ${LDADD_gtest} DPADD_gmock_main+= ${DPADD_gmock} LDADD_gmock_main+= ${LDADD_gmock} DPADD_gtest_main+= ${DPADD_gtest} LDADD_gtest_main+= ${LDADD_gtest} # Detect LDADD/DPADD that should be LIBADD, before modifying LDADD here. _BADLDADD= .for _l in ${LDADD:M-l*:N-l*/*:C,^-l,,} .if ${_LIBRARIES:M${_l}} && !${_PRIVATELIBS:M${_l}} _BADLDADD+= ${_l} .endif .endfor .if !empty(_BADLDADD) .error ${.CURDIR}: These libraries should be LIBADD+=foo rather than DPADD/LDADD+=-lfoo: ${_BADLDADD} .endif .for _l in ${LIBADD} DPADD+= ${DPADD_${_l}} LDADD+= ${LDADD_${_l}} .endfor _LIB_OBJTOP?= ${OBJTOP} # INTERNALLIB definitions. LIBELFTCDIR= ${_LIB_OBJTOP}/lib/libelftc LIBELFTC?= ${LIBELFTCDIR}/libelftc${PIE_SUFFIX}.a LIBKYUA_CLIDIR= ${_LIB_OBJTOP}/lib/kyua/cli LIBKYUA_CLI?= ${LIBKYUA_CLIDIR}/libkyua_cli${PIE_SUFFIX}.a LIBKYUA_DRIVERSDIR= ${_LIB_OBJTOP}/lib/kyua/drivers LIBKYUA_DRIVERS?= ${LIBKYUA_DRIVERSDIR}/libkyua_drivers${PIE_SUFFIX}.a LIBKYUA_ENGINEDIR= ${_LIB_OBJTOP}/lib/kyua/engine LIBKYUA_ENGINE?= ${LIBKYUA_ENGINEDIR}/libkyua_engine${PIE_SUFFIX}.a LIBKYUA_MODELDIR= ${_LIB_OBJTOP}/lib/kyua/model LIBKYUA_MODEL?= ${LIBKYUA_MODELDIR}/libkyua_model${PIE_SUFFIX}.a LIBKYUA_STOREDIR= ${_LIB_OBJTOP}/lib/kyua/store LIBKYUA_STORE?= ${LIBKYUA_STOREDIR}/libkyua_store${PIE_SUFFIX}.a LIBKYUA_UTILSDIR= ${_LIB_OBJTOP}/lib/kyua/utils LIBKYUA_UTILS?= ${LIBKYUA_UTILSDIR}/libkyua_utils${PIE_SUFFIX}.a LIBLUADIR= ${_LIB_OBJTOP}/lib/liblua LIBLUA?= ${LIBLUADIR}/liblua${PIE_SUFFIX}.a LIBLUTOKDIR= ${_LIB_OBJTOP}/lib/liblutok LIBLUTOK?= ${LIBLUTOKDIR}/liblutok${PIE_SUFFIX}.a LIBPEDIR= ${_LIB_OBJTOP}/lib/libpe LIBPE?= ${LIBPEDIR}/libpe${PIE_SUFFIX}.a LIBOPENBSDDIR= ${_LIB_OBJTOP}/lib/libopenbsd LIBOPENBSD?= ${LIBOPENBSDDIR}/libopenbsd${PIE_SUFFIX}.a LIBSMDIR= ${_LIB_OBJTOP}/lib/libsm LIBSM?= ${LIBSMDIR}/libsm${PIE_SUFFIX}.a LIBSMDBDIR= ${_LIB_OBJTOP}/lib/libsmdb LIBSMDB?= ${LIBSMDBDIR}/libsmdb${PIE_SUFFIX}.a LIBSMUTILDIR= ${_LIB_OBJTOP}/lib/libsmutil LIBSMUTIL?= ${LIBSMUTILDIR}/libsmutil${PIE_SUFFIX}.a LIBNETBSDDIR?= ${_LIB_OBJTOP}/lib/libnetbsd LIBNETBSD?= ${LIBNETBSDDIR}/libnetbsd${PIE_SUFFIX}.a LIBVERSDIR?= ${_LIB_OBJTOP}/kerberos5/lib/libvers LIBVERS?= ${LIBVERSDIR}/libvers${PIE_SUFFIX}.a LIBSLDIR= ${_LIB_OBJTOP}/kerberos5/lib/libsl LIBSL?= ${LIBSLDIR}/libsl${PIE_SUFFIX}.a LIBIFCONFIGDIR= ${_LIB_OBJTOP}/lib/libifconfig LIBIFCONFIG?= ${LIBIFCONFIGDIR}/libifconfig${PIE_SUFFIX}.a LIBIPFDIR= ${_LIB_OBJTOP}/sbin/ipf/libipf LIBIPF?= ${LIBIPFDIR}/libipf${PIE_SUFFIX}.a LIBTELNETDIR= ${_LIB_OBJTOP}/lib/libtelnet LIBTELNET?= ${LIBTELNETDIR}/libtelnet${PIE_SUFFIX}.a LIBCRONDIR= ${_LIB_OBJTOP}/usr.sbin/cron/lib LIBCRON?= ${LIBCRONDIR}/libcron${PIE_SUFFIX}.a LIBNTPDIR= ${_LIB_OBJTOP}/usr.sbin/ntp/libntp LIBNTP?= ${LIBNTPDIR}/libntp${PIE_SUFFIX}.a LIBNTPEVENTDIR= ${_LIB_OBJTOP}/usr.sbin/ntp/libntpevent LIBNTPEVENT?= ${LIBNTPEVENTDIR}/libntpevent${PIE_SUFFIX}.a LIBOPTSDIR= ${_LIB_OBJTOP}/usr.sbin/ntp/libopts LIBOPTS?= ${LIBOPTSDIR}/libopts${PIE_SUFFIX}.a LIBPARSEDIR= ${_LIB_OBJTOP}/usr.sbin/ntp/libparse LIBPARSE?= ${LIBPARSEDIR}/libparse${PIE_SUFFIX}.a LIBLPRDIR= ${_LIB_OBJTOP}/usr.sbin/lpr/common_source LIBLPR?= ${LIBLPRDIR}/liblpr${PIE_SUFFIX}.a LIBFIFOLOGDIR= ${_LIB_OBJTOP}/usr.sbin/fifolog/lib LIBFIFOLOG?= ${LIBFIFOLOGDIR}/libfifolog${PIE_SUFFIX}.a LIBBSNMPTOOLSDIR= ${_LIB_OBJTOP}/usr.sbin/bsnmpd/tools/libbsnmptools LIBBSNMPTOOLS?= ${LIBBSNMPTOOLSDIR}/libbsnmptools${PIE_SUFFIX}.a LIBAMUDIR= ${_LIB_OBJTOP}/usr.sbin/amd/libamu LIBAMU?= ${LIBAMUDIR}/libamu${PIE_SUFFIX}.a LIBBE?= ${LIBBEDIR}/libbe${PIE_SUFFIX}.a LIBPMCSTATDIR= ${_LIB_OBJTOP}/lib/libpmcstat LIBPMCSTAT?= ${LIBPMCSTATDIR}/libpmcstat${PIE_SUFFIX}.a LIBC_NOSSP_PICDIR= ${_LIB_OBJTOP}/lib/libc LIBC_NOSSP_PIC?= ${LIBC_NOSSP_PICDIR}/libc_nossp_pic.a # Define a directory for each library. This is useful for adding -L in when # not using a --sysroot or for meta mode bootstrapping when there is no # Makefile.depend. These are sorted by directory. LIBAVLDIR= ${OBJTOP}/cddl/lib/libavl LIBCTFDIR= ${OBJTOP}/cddl/lib/libctf LIBDTRACEDIR= ${OBJTOP}/cddl/lib/libdtrace LIBNVPAIRDIR= ${OBJTOP}/cddl/lib/libnvpair LIBUMEMDIR= ${OBJTOP}/cddl/lib/libumem LIBUUTILDIR= ${OBJTOP}/cddl/lib/libuutil LIBZFSDIR= ${OBJTOP}/cddl/lib/libzfs LIBZFS_COREDIR= ${OBJTOP}/cddl/lib/libzfs_core LIBZPOOLDIR= ${OBJTOP}/cddl/lib/libzpool # OFED support LIBCXGB4DIR= ${OBJTOP}/lib/ofed/libcxgb4 LIBIBCMDIR= ${OBJTOP}/lib/ofed/libibcm LIBIBMADDIR= ${OBJTOP}/lib/ofed/libibmad LIBIBNETDISCDIR=${OBJTOP}/lib/ofed/libibnetdisc LIBIBUMADDIR= ${OBJTOP}/lib/ofed/libibumad LIBIBVERBSDIR= ${OBJTOP}/lib/ofed/libibverbs LIBMLX4DIR= ${OBJTOP}/lib/ofed/libmlx4 LIBMLX5DIR= ${OBJTOP}/lib/ofed/libmlx5 LIBRDMACMDIR= ${OBJTOP}/lib/ofed/librdmacm LIBOSMCOMPDIR= ${OBJTOP}/lib/ofed/complib LIBOPENSMDIR= ${OBJTOP}/lib/ofed/libopensm LIBOSMVENDORDIR=${OBJTOP}/lib/ofed/libvendor LIBDIALOGDIR= ${OBJTOP}/gnu/lib/libdialog LIBGNUREGEXDIR= ${OBJTOP}/gnu/lib/libregex LIBSSPDIR= ${OBJTOP}/lib/libssp LIBSSP_NONSHAREDDIR= ${OBJTOP}/lib/libssp_nonshared LIBASN1DIR= ${OBJTOP}/kerberos5/lib/libasn1 LIBGSSAPI_KRB5DIR= ${OBJTOP}/kerberos5/lib/libgssapi_krb5 LIBGSSAPI_NTLMDIR= ${OBJTOP}/kerberos5/lib/libgssapi_ntlm LIBGSSAPI_SPNEGODIR= ${OBJTOP}/kerberos5/lib/libgssapi_spnego LIBHDBDIR= ${OBJTOP}/kerberos5/lib/libhdb LIBHEIMBASEDIR= ${OBJTOP}/kerberos5/lib/libheimbase LIBHEIMIPCCDIR= ${OBJTOP}/kerberos5/lib/libheimipcc LIBHEIMIPCSDIR= ${OBJTOP}/kerberos5/lib/libheimipcs LIBHEIMNTLMDIR= ${OBJTOP}/kerberos5/lib/libheimntlm LIBHX509DIR= ${OBJTOP}/kerberos5/lib/libhx509 LIBKADM5CLNTDIR= ${OBJTOP}/kerberos5/lib/libkadm5clnt LIBKADM5SRVDIR= ${OBJTOP}/kerberos5/lib/libkadm5srv LIBKAFS5DIR= ${OBJTOP}/kerberos5/lib/libkafs5 LIBKDCDIR= ${OBJTOP}/kerberos5/lib/libkdc LIBKRB5DIR= ${OBJTOP}/kerberos5/lib/libkrb5 LIBROKENDIR= ${OBJTOP}/kerberos5/lib/libroken LIBWINDDIR= ${OBJTOP}/kerberos5/lib/libwind LIBATF_CDIR= ${OBJTOP}/lib/atf/libatf-c LIBATF_CXXDIR= ${OBJTOP}/lib/atf/libatf-c++ LIBGMOCKDIR= ${OBJTOP}/lib/googletest/gmock LIBGMOCK_MAINDIR= ${OBJTOP}/lib/googletest/gmock_main LIBGTESTDIR= ${OBJTOP}/lib/googletest/gtest LIBGTEST_MAINDIR= ${OBJTOP}/lib/googletest/gtest_main LIBALIASDIR= ${OBJTOP}/lib/libalias/libalias LIBBLACKLISTDIR= ${OBJTOP}/lib/libblacklist LIBBLOCKSRUNTIMEDIR= ${OBJTOP}/lib/libblocksruntime LIBBSNMPDIR= ${OBJTOP}/lib/libbsnmp/libbsnmp 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 LIBBSDXMLDIR= ${OBJTOP}/lib/libexpat LIBKVMDIR= ${OBJTOP}/lib/libkvm LIBPTHREADDIR= ${OBJTOP}/lib/libthr LIBMDIR= ${OBJTOP}/lib/msun LIBFORMDIR= ${OBJTOP}/lib/ncurses/form LIBFORMLIBWDIR= ${OBJTOP}/lib/ncurses/formw LIBMENUDIR= ${OBJTOP}/lib/ncurses/menu LIBMENULIBWDIR= ${OBJTOP}/lib/ncurses/menuw LIBNCURSESDIR= ${OBJTOP}/lib/ncurses/ncurses LIBNCURSESWDIR= ${OBJTOP}/lib/ncurses/ncursesw LIBPANELDIR= ${OBJTOP}/lib/ncurses/panel LIBPANELWDIR= ${OBJTOP}/lib/ncurses/panelw LIBCRYPTODIR= ${OBJTOP}/secure/lib/libcrypto LIBSSHDIR= ${OBJTOP}/secure/lib/libssh LIBSSLDIR= ${OBJTOP}/secure/lib/libssl LIBTEKENDIR= ${OBJTOP}/sys/teken/libteken LIBEGACYDIR= ${OBJTOP}/tools/build LIBLNDIR= ${OBJTOP}/usr.bin/lex/lib LIBTERMCAPDIR= ${LIBNCURSESDIR} LIBTERMCAPWDIR= ${LIBNCURSESWDIR} # Default other library directories to lib/libNAME. .for lib in ${_LIBRARIES} LIB${lib:tu}DIR?= ${OBJTOP}/lib/lib${lib} .endfor # Validate that listed LIBADD are valid. .for _l in ${LIBADD} .if empty(_LIBRARIES:M${_l}) _BADLIBADD+= ${_l} .endif .endfor .if !empty(_BADLIBADD) .error ${.CURDIR}: Invalid LIBADD used which may need to be added to ${_this:T}: ${_BADLIBADD} .endif # Sanity check that libraries are defined here properly when building them. .if defined(LIB) && ${_LIBRARIES:M${LIB}} != "" .if !empty(LIBADD) && \ (!defined(_DP_${LIB}) || ${LIBADD:O:u} != ${_DP_${LIB}:O:u}) .error ${.CURDIR}: Missing or incorrect _DP_${LIB} entry in ${_this:T}. Should match LIBADD for ${LIB} ('${LIBADD}' vs '${_DP_${LIB}}') .endif # Note that OBJTOP is not yet defined here but for the purpose of the check # it is fine as it resolves to the SRC directory. .if !defined(LIB${LIB:tu}DIR) || !exists(${SRCTOP}/${LIB${LIB:tu}DIR:S,^${OBJTOP}/,,}) .error ${.CURDIR}: Missing or incorrect value for LIB${LIB:tu}DIR in ${_this:T}: ${LIB${LIB:tu}DIR:S,^${OBJTOP}/,,} .endif .if ${_INTERNALLIBS:M${LIB}} != "" && !defined(LIB${LIB:tu}) .error ${.CURDIR}: Missing value for LIB${LIB:tu} in ${_this:T}. Likely should be: LIB${LIB:tu}?= $${LIB${LIB:tu}DIR}/lib${LIB}.a .endif .endif .endif # !target(____)