xref: /freebsd/lib/libcasper/services/cap_net/cap_net.c (revision b60053fde1726ea047aadfbc0354b79e99d4a668)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2020 Mariusz Zaborski <oshogbo@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 #include <sys/cnv.h>
30 #include <sys/dnv.h>
31 #include <sys/nv.h>
32 #include <sys/socket.h>
33 #include <netinet/in.h>
34 
35 #include <assert.h>
36 #include <errno.h>
37 #include <netdb.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <unistd.h>
41 
42 #include <libcasper.h>
43 #include <libcasper_service.h>
44 
45 #include "cap_net.h"
46 
47 #define	CAPNET_MASK	(CAPNET_ADDR2NAME | CAPNET_NAME2ADDR	\
48     CAPNET_DEPRECATED_ADDR2NAME | CAPNET_DEPRECATED_NAME2ADDR | \
49     CAPNET_CONNECT | CAPNET_BIND | CAPNET_CONNECTDNS)
50 
51 /*
52  * Defines for the names of the limits.
53  * XXX: we should convert all string constats to this to avoid typos.
54  */
55 #define	LIMIT_NV_BIND			"bind"
56 #define	LIMIT_NV_CONNECT		"connect"
57 #define	LIMIT_NV_ADDR2NAME		"addr2name"
58 #define	LIMIT_NV_NAME2ADDR		"name2addr"
59 
60 struct cap_net_limit {
61 	cap_channel_t	*cnl_chan;
62 	uint64_t	 cnl_mode;
63 	nvlist_t	*cnl_addr2name;
64 	nvlist_t	*cnl_name2addr;
65 	nvlist_t	*cnl_connect;
66 	nvlist_t	*cnl_bind;
67 };
68 
69 static struct hostent hent;
70 
71 static void
hostent_free(struct hostent * hp)72 hostent_free(struct hostent *hp)
73 {
74 	unsigned int ii;
75 
76 	free(hp->h_name);
77 	hp->h_name = NULL;
78 	if (hp->h_aliases != NULL) {
79 		for (ii = 0; hp->h_aliases[ii] != NULL; ii++)
80 			free(hp->h_aliases[ii]);
81 		free(hp->h_aliases);
82 		hp->h_aliases = NULL;
83 	}
84 	if (hp->h_addr_list != NULL) {
85 		for (ii = 0; hp->h_addr_list[ii] != NULL; ii++)
86 			free(hp->h_addr_list[ii]);
87 		free(hp->h_addr_list);
88 		hp->h_addr_list = NULL;
89 	}
90 }
91 
92 static struct hostent *
hostent_unpack(const nvlist_t * nvl,struct hostent * hp)93 hostent_unpack(const nvlist_t *nvl, struct hostent *hp)
94 {
95 	unsigned int ii, nitems;
96 	char nvlname[64];
97 	int n;
98 
99 	hostent_free(hp);
100 
101 	hp->h_name = strdup(nvlist_get_string(nvl, "name"));
102 	if (hp->h_name == NULL)
103 		goto fail;
104 	hp->h_addrtype = (int)nvlist_get_number(nvl, "addrtype");
105 	hp->h_length = (int)nvlist_get_number(nvl, "length");
106 
107 	nitems = (unsigned int)nvlist_get_number(nvl, "naliases");
108 	hp->h_aliases = calloc(nitems + 1, sizeof(hp->h_aliases[0]));
109 	if (hp->h_aliases == NULL)
110 		goto fail;
111 	for (ii = 0; ii < nitems; ii++) {
112 		n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
113 		assert(n > 0 && n < (int)sizeof(nvlname));
114 		hp->h_aliases[ii] =
115 		    strdup(nvlist_get_string(nvl, nvlname));
116 		if (hp->h_aliases[ii] == NULL)
117 			goto fail;
118 	}
119 	hp->h_aliases[ii] = NULL;
120 
121 	nitems = (unsigned int)nvlist_get_number(nvl, "naddrs");
122 	hp->h_addr_list = calloc(nitems + 1, sizeof(hp->h_addr_list[0]));
123 	if (hp->h_addr_list == NULL)
124 		goto fail;
125 	for (ii = 0; ii < nitems; ii++) {
126 		hp->h_addr_list[ii] = malloc(hp->h_length);
127 		if (hp->h_addr_list[ii] == NULL)
128 			goto fail;
129 		n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
130 		assert(n > 0 && n < (int)sizeof(nvlname));
131 		bcopy(nvlist_get_binary(nvl, nvlname, NULL),
132 		    hp->h_addr_list[ii], hp->h_length);
133 	}
134 	hp->h_addr_list[ii] = NULL;
135 
136 	return (hp);
137 fail:
138 	hostent_free(hp);
139 	h_errno = NO_RECOVERY;
140 	return (NULL);
141 }
142 
143 static int
request_cb(cap_channel_t * chan,const char * name,int s,const struct sockaddr * saddr,socklen_t len)144 request_cb(cap_channel_t *chan, const char *name, int s,
145     const struct sockaddr *saddr, socklen_t len)
146 {
147 	nvlist_t *nvl;
148 	int serrno;
149 
150 	nvl = nvlist_create(0);
151 	nvlist_add_string(nvl, "cmd", name);
152 	nvlist_add_descriptor(nvl, "s", s);
153 	nvlist_add_binary(nvl, "saddr", saddr, len);
154 
155 	nvl = cap_xfer_nvlist(chan, nvl);
156 	if (nvl == NULL)
157 		return (-1);
158 
159 	if (nvlist_get_number(nvl, "error") != 0) {
160 		serrno = (int)nvlist_get_number(nvl, "error");
161 		nvlist_destroy(nvl);
162 		errno = serrno;
163 		return (-1);
164 	}
165 
166 	s = dup2(s, nvlist_get_descriptor(nvl, "s"));
167 	nvlist_destroy(nvl);
168 
169 	return (s == -1 ? -1 : 0);
170 }
171 
172 int
cap_bind(cap_channel_t * chan,int s,const struct sockaddr * addr,socklen_t addrlen)173 cap_bind(cap_channel_t *chan, int s, const struct sockaddr *addr,
174     socklen_t addrlen)
175 {
176 
177 	return (request_cb(chan, LIMIT_NV_BIND, s, addr, addrlen));
178 }
179 
180 int
cap_connect(cap_channel_t * chan,int s,const struct sockaddr * name,socklen_t namelen)181 cap_connect(cap_channel_t *chan, int s, const struct sockaddr *name,
182     socklen_t namelen)
183 {
184 
185 	return (request_cb(chan, LIMIT_NV_CONNECT, s, name, namelen));
186 }
187 
188 
189 struct hostent *
cap_gethostbyname(cap_channel_t * chan,const char * name)190 cap_gethostbyname(cap_channel_t *chan, const char *name)
191 {
192 
193 	return (cap_gethostbyname2(chan, name, AF_INET));
194 }
195 
196 struct hostent *
cap_gethostbyname2(cap_channel_t * chan,const char * name,int af)197 cap_gethostbyname2(cap_channel_t *chan, const char *name, int af)
198 {
199 	struct hostent *hp;
200 	nvlist_t *nvl;
201 
202 	nvl = nvlist_create(0);
203 	nvlist_add_string(nvl, "cmd", "gethostbyname");
204 	nvlist_add_number(nvl, "family", (uint64_t)af);
205 	nvlist_add_string(nvl, "name", name);
206 	nvl = cap_xfer_nvlist(chan, nvl);
207 	if (nvl == NULL) {
208 		h_errno = NO_RECOVERY;
209 		return (NULL);
210 	}
211 	if (nvlist_get_number(nvl, "error") != 0) {
212 		h_errno = (int)nvlist_get_number(nvl, "error");
213 		nvlist_destroy(nvl);
214 		return (NULL);
215 	}
216 
217 	hp = hostent_unpack(nvl, &hent);
218 	nvlist_destroy(nvl);
219 	return (hp);
220 }
221 
222 struct hostent *
cap_gethostbyaddr(cap_channel_t * chan,const void * addr,socklen_t len,int af)223 cap_gethostbyaddr(cap_channel_t *chan, const void *addr, socklen_t len,
224     int af)
225 {
226 	struct hostent *hp;
227 	nvlist_t *nvl;
228 
229 	nvl = nvlist_create(0);
230 	nvlist_add_string(nvl, "cmd", "gethostbyaddr");
231 	nvlist_add_binary(nvl, "addr", addr, (size_t)len);
232 	nvlist_add_number(nvl, "family", (uint64_t)af);
233 	nvl = cap_xfer_nvlist(chan, nvl);
234 	if (nvl == NULL) {
235 		h_errno = NO_RECOVERY;
236 		return (NULL);
237 	}
238 	if (nvlist_get_number(nvl, "error") != 0) {
239 		h_errno = (int)nvlist_get_number(nvl, "error");
240 		nvlist_destroy(nvl);
241 		return (NULL);
242 	}
243 	hp = hostent_unpack(nvl, &hent);
244 	nvlist_destroy(nvl);
245 	return (hp);
246 }
247 
248 static struct addrinfo *
addrinfo_unpack(const nvlist_t * nvl)249 addrinfo_unpack(const nvlist_t *nvl)
250 {
251 	struct addrinfo *ai;
252 	const void *addr;
253 	size_t addrlen;
254 	const char *canonname;
255 
256 	addr = nvlist_get_binary(nvl, "ai_addr", &addrlen);
257 	ai = malloc(sizeof(*ai) + addrlen);
258 	if (ai == NULL)
259 		return (NULL);
260 	ai->ai_flags = (int)nvlist_get_number(nvl, "ai_flags");
261 	ai->ai_family = (int)nvlist_get_number(nvl, "ai_family");
262 	ai->ai_socktype = (int)nvlist_get_number(nvl, "ai_socktype");
263 	ai->ai_protocol = (int)nvlist_get_number(nvl, "ai_protocol");
264 	ai->ai_addrlen = (socklen_t)addrlen;
265 	canonname = dnvlist_get_string(nvl, "ai_canonname", NULL);
266 	if (canonname != NULL) {
267 		ai->ai_canonname = strdup(canonname);
268 		if (ai->ai_canonname == NULL) {
269 			free(ai);
270 			return (NULL);
271 		}
272 	} else {
273 		ai->ai_canonname = NULL;
274 	}
275 	ai->ai_addr = (void *)(ai + 1);
276 	bcopy(addr, ai->ai_addr, addrlen);
277 	ai->ai_next = NULL;
278 
279 	return (ai);
280 }
281 
282 int
cap_getaddrinfo(cap_channel_t * chan,const char * hostname,const char * servname,const struct addrinfo * hints,struct addrinfo ** res)283 cap_getaddrinfo(cap_channel_t *chan, const char *hostname, const char *servname,
284     const struct addrinfo *hints, struct addrinfo **res)
285 {
286 	struct addrinfo *firstai, *prevai, *curai;
287 	unsigned int ii;
288 	const nvlist_t *nvlai;
289 	char nvlname[64];
290 	nvlist_t *nvl;
291 	int error, serrno, n;
292 
293 	nvl = nvlist_create(0);
294 	nvlist_add_string(nvl, "cmd", "getaddrinfo");
295 	if (hostname != NULL)
296 		nvlist_add_string(nvl, "hostname", hostname);
297 	if (servname != NULL)
298 		nvlist_add_string(nvl, "servname", servname);
299 	if (hints != NULL) {
300 		nvlist_add_number(nvl, "hints.ai_flags",
301 		    (uint64_t)hints->ai_flags);
302 		nvlist_add_number(nvl, "hints.ai_family",
303 		    (uint64_t)hints->ai_family);
304 		nvlist_add_number(nvl, "hints.ai_socktype",
305 		    (uint64_t)hints->ai_socktype);
306 		nvlist_add_number(nvl, "hints.ai_protocol",
307 		    (uint64_t)hints->ai_protocol);
308 	}
309 	nvl = cap_xfer_nvlist(chan, nvl);
310 	if (nvl == NULL)
311 		return (EAI_MEMORY);
312 	if (nvlist_get_number(nvl, "error") != 0) {
313 		error = (int)nvlist_get_number(nvl, "error");
314 		serrno = dnvlist_get_number(nvl, "errno", 0);
315 		nvlist_destroy(nvl);
316 		errno = (error == EAI_SYSTEM) ? serrno : 0;
317 		return (error);
318 	}
319 
320 	nvlai = NULL;
321 	firstai = prevai = curai = NULL;
322 	for (ii = 0; ; ii++) {
323 		n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
324 		assert(n > 0 && n < (int)sizeof(nvlname));
325 		if (!nvlist_exists_nvlist(nvl, nvlname))
326 			break;
327 		nvlai = nvlist_get_nvlist(nvl, nvlname);
328 		curai = addrinfo_unpack(nvlai);
329 		if (curai == NULL) {
330 			nvlist_destroy(nvl);
331 			return (EAI_MEMORY);
332 		}
333 		if (prevai != NULL)
334 			prevai->ai_next = curai;
335 		else
336 			firstai = curai;
337 		prevai = curai;
338 	}
339 	nvlist_destroy(nvl);
340 	if (curai == NULL && nvlai != NULL) {
341 		if (firstai == NULL)
342 			freeaddrinfo(firstai);
343 		return (EAI_MEMORY);
344 	}
345 
346 	*res = firstai;
347 	return (0);
348 }
349 
350 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)351 cap_getnameinfo(cap_channel_t *chan, const struct sockaddr *sa, socklen_t salen,
352     char *host, size_t hostlen, char *serv, size_t servlen, int flags)
353 {
354 	nvlist_t *nvl;
355 	int error, serrno;
356 
357 	nvl = nvlist_create(0);
358 	nvlist_add_string(nvl, "cmd", "getnameinfo");
359 	nvlist_add_number(nvl, "hostlen", (uint64_t)hostlen);
360 	nvlist_add_number(nvl, "servlen", (uint64_t)servlen);
361 	nvlist_add_binary(nvl, "sa", sa, (size_t)salen);
362 	nvlist_add_number(nvl, "flags", (uint64_t)flags);
363 	nvl = cap_xfer_nvlist(chan, nvl);
364 	if (nvl == NULL)
365 		return (EAI_MEMORY);
366 	if (nvlist_get_number(nvl, "error") != 0) {
367 		error = (int)nvlist_get_number(nvl, "error");
368 		serrno = dnvlist_get_number(nvl, "errno", 0);
369 		nvlist_destroy(nvl);
370 		errno = (error == EAI_SYSTEM) ? serrno : 0;
371 		return (error);
372 	}
373 
374 	if (host != NULL && nvlist_exists_string(nvl, "host"))
375 		strlcpy(host, nvlist_get_string(nvl, "host"), hostlen);
376 	if (serv != NULL && nvlist_exists_string(nvl, "serv"))
377 		strlcpy(serv, nvlist_get_string(nvl, "serv"), servlen);
378 	nvlist_destroy(nvl);
379 	return (0);
380 }
381 
382 cap_net_limit_t *
cap_net_limit_init(cap_channel_t * chan,uint64_t mode)383 cap_net_limit_init(cap_channel_t *chan, uint64_t mode)
384 {
385 	cap_net_limit_t *limit;
386 
387 	limit = calloc(1, sizeof(*limit));
388 	if (limit != NULL) {
389 		limit->cnl_mode = mode;
390 		limit->cnl_chan = chan;
391 		limit->cnl_addr2name = nvlist_create(0);
392 		limit->cnl_name2addr = nvlist_create(0);
393 		limit->cnl_connect = nvlist_create(0);
394 		limit->cnl_bind = nvlist_create(0);
395 	}
396 
397 	return (limit);
398 }
399 
400 static void
pack_limit(nvlist_t * lnvl,const char * name,nvlist_t * limit)401 pack_limit(nvlist_t *lnvl, const char *name, nvlist_t *limit)
402 {
403 
404 	if (!nvlist_empty(limit)) {
405 		nvlist_move_nvlist(lnvl, name, limit);
406 	} else {
407 		nvlist_destroy(limit);
408 	}
409 }
410 
411 int
cap_net_limit(cap_net_limit_t * limit)412 cap_net_limit(cap_net_limit_t *limit)
413 {
414 	nvlist_t *lnvl;
415 	cap_channel_t *chan;
416 
417 	lnvl = nvlist_create(0);
418 	nvlist_add_number(lnvl, "mode", limit->cnl_mode);
419 
420 	pack_limit(lnvl, LIMIT_NV_ADDR2NAME, limit->cnl_addr2name);
421 	pack_limit(lnvl, LIMIT_NV_NAME2ADDR, limit->cnl_name2addr);
422 	pack_limit(lnvl, LIMIT_NV_CONNECT, limit->cnl_connect);
423 	pack_limit(lnvl, LIMIT_NV_BIND, limit->cnl_bind);
424 
425 	chan = limit->cnl_chan;
426 	free(limit);
427 
428 	return (cap_limit_set(chan, lnvl));
429 }
430 
431 void
cap_net_free(cap_net_limit_t * limit)432 cap_net_free(cap_net_limit_t *limit)
433 {
434 
435 	if (limit == NULL)
436 		return;
437 
438 	nvlist_destroy(limit->cnl_addr2name);
439 	nvlist_destroy(limit->cnl_name2addr);
440 	nvlist_destroy(limit->cnl_connect);
441 	nvlist_destroy(limit->cnl_bind);
442 
443 	free(limit);
444 }
445 
446 static void
pack_family(nvlist_t * nvl,int * family,size_t size)447 pack_family(nvlist_t *nvl, int *family, size_t size)
448 {
449 	size_t i;
450 
451 	i = 0;
452 	if (!nvlist_exists_number_array(nvl, "family")) {
453 		uint64_t val;
454 
455 		val = family[0];
456 		nvlist_add_number_array(nvl, "family", &val, 1);
457 		i += 1;
458 	}
459 
460 	for (; i < size; i++) {
461 		nvlist_append_number_array(nvl, "family", family[i]);
462 	}
463 }
464 
465 static void
pack_sockaddr(nvlist_t * res,const struct sockaddr * sa,socklen_t salen)466 pack_sockaddr(nvlist_t *res, const struct sockaddr *sa, socklen_t salen)
467 {
468 	nvlist_t *nvl;
469 
470 	if (!nvlist_exists_nvlist(res, "sockaddr")) {
471 		nvl = nvlist_create(NV_FLAG_NO_UNIQUE);
472 	} else {
473 		nvl = nvlist_take_nvlist(res, "sockaddr");
474 	}
475 
476 	nvlist_add_binary(nvl, "", sa, salen);
477 	nvlist_move_nvlist(res, "sockaddr", nvl);
478 }
479 
480 cap_net_limit_t *
cap_net_limit_addr2name_family(cap_net_limit_t * limit,int * family,size_t size)481 cap_net_limit_addr2name_family(cap_net_limit_t *limit, int *family, size_t size)
482 {
483 
484 	pack_family(limit->cnl_addr2name, family, size);
485 	return (limit);
486 }
487 
488 cap_net_limit_t *
cap_net_limit_name2addr_family(cap_net_limit_t * limit,int * family,size_t size)489 cap_net_limit_name2addr_family(cap_net_limit_t *limit, int *family, size_t size)
490 {
491 
492 	pack_family(limit->cnl_name2addr, family, size);
493 	return (limit);
494 }
495 
496 cap_net_limit_t *
cap_net_limit_name2addr(cap_net_limit_t * limit,const char * host,const char * serv)497 cap_net_limit_name2addr(cap_net_limit_t *limit, const char *host,
498     const char *serv)
499 {
500 	nvlist_t *nvl;
501 
502 	if (!nvlist_exists_nvlist(limit->cnl_name2addr, "hosts")) {
503 		nvl = nvlist_create(NV_FLAG_NO_UNIQUE);
504 	} else {
505 		nvl = nvlist_take_nvlist(limit->cnl_name2addr, "hosts");
506 	}
507 
508 	nvlist_add_string(nvl,
509 	    host != NULL ? host : "",
510 	    serv != NULL ? serv : "");
511 
512 	nvlist_move_nvlist(limit->cnl_name2addr, "hosts", nvl);
513 	return (limit);
514 }
515 
516 cap_net_limit_t *
cap_net_limit_addr2name(cap_net_limit_t * limit,const struct sockaddr * sa,socklen_t salen)517 cap_net_limit_addr2name(cap_net_limit_t *limit, const struct sockaddr *sa,
518     socklen_t salen)
519 {
520 
521 	pack_sockaddr(limit->cnl_addr2name, sa, salen);
522 	return (limit);
523 }
524 
525 
526 cap_net_limit_t *
cap_net_limit_connect(cap_net_limit_t * limit,const struct sockaddr * sa,socklen_t salen)527 cap_net_limit_connect(cap_net_limit_t *limit, const struct sockaddr *sa,
528     socklen_t salen)
529 {
530 
531 	pack_sockaddr(limit->cnl_connect, sa, salen);
532 	return (limit);
533 }
534 
535 cap_net_limit_t *
cap_net_limit_bind(cap_net_limit_t * limit,const struct sockaddr * sa,socklen_t salen)536 cap_net_limit_bind(cap_net_limit_t *limit, const struct sockaddr *sa,
537     socklen_t salen)
538 {
539 
540 	pack_sockaddr(limit->cnl_bind, sa, salen);
541 	return (limit);
542 }
543 
544 /*
545  * Service functions.
546  */
547 
548 static nvlist_t *capdnscache;
549 
550 static void
net_add_sockaddr_to_cache(struct sockaddr * sa,socklen_t salen,bool deprecated)551 net_add_sockaddr_to_cache(struct sockaddr *sa, socklen_t salen, bool deprecated)
552 {
553 	void *cookie;
554 
555 	if (capdnscache == NULL) {
556 		capdnscache = nvlist_create(NV_FLAG_NO_UNIQUE);
557 	} else {
558 		/* Lets keep it clean. Look for dups. */
559 		cookie = NULL;
560 		while (nvlist_next(capdnscache, NULL, &cookie) != NULL) {
561 			const void *data;
562 			size_t size;
563 
564 			assert(cnvlist_type(cookie) == NV_TYPE_BINARY);
565 
566 			data = cnvlist_get_binary(cookie, &size);
567 			if (salen != size)
568 				continue;
569 			if (memcmp(data, sa, size) == 0)
570 				return;
571 		}
572 	}
573 
574 	nvlist_add_binary(capdnscache, deprecated ? "d" : "", sa, salen);
575 }
576 
577 static void
net_add_hostent_to_cache(const char * address,size_t asize,int family)578 net_add_hostent_to_cache(const char *address, size_t asize, int family)
579 {
580 
581 	if (family != AF_INET && family != AF_INET6)
582 		return;
583 
584 	if (family == AF_INET6) {
585 		struct sockaddr_in6 connaddr;
586 
587 		memset(&connaddr, 0, sizeof(connaddr));
588 		connaddr.sin6_family = AF_INET6;
589 		memcpy((char *)&connaddr.sin6_addr, address, asize);
590 		connaddr.sin6_port = 0;
591 
592 		net_add_sockaddr_to_cache((struct sockaddr *)&connaddr,
593 		    sizeof(connaddr), true);
594 	} else {
595 		struct sockaddr_in connaddr;
596 
597 		memset(&connaddr, 0, sizeof(connaddr));
598 		connaddr.sin_family = AF_INET;
599 		memcpy((char *)&connaddr.sin_addr.s_addr, address, asize);
600 		connaddr.sin_port = 0;
601 
602 		net_add_sockaddr_to_cache((struct sockaddr *)&connaddr,
603 		    sizeof(connaddr), true);
604 	}
605 }
606 
607 static bool
net_allowed_mode(const nvlist_t * limits,uint64_t mode)608 net_allowed_mode(const nvlist_t *limits, uint64_t mode)
609 {
610 
611 	if (limits == NULL)
612 		return (true);
613 
614 	return ((nvlist_get_number(limits, "mode") & mode) == mode);
615 }
616 
617 static bool
net_allowed_family(const nvlist_t * limits,int family)618 net_allowed_family(const nvlist_t *limits, int family)
619 {
620 	const uint64_t *allowedfamily;
621 	size_t i, allsize;
622 
623 	if (limits == NULL)
624 		return (true);
625 
626 	/* If there are no familes at all, allow any mode. */
627 	if (!nvlist_exists_number_array(limits, "family"))
628 		return (true);
629 
630 	allowedfamily = nvlist_get_number_array(limits, "family", &allsize);
631 	for (i = 0; i < allsize; i++) {
632 		/* XXX: what with AF_UNSPEC? */
633 		if (allowedfamily[i] == (uint64_t)family) {
634 			return (true);
635 		}
636 	}
637 
638 	return (false);
639 }
640 
641 static bool
net_allowed_bsaddr_impl(const nvlist_t * salimits,const void * saddr,size_t saddrsize)642 net_allowed_bsaddr_impl(const nvlist_t *salimits, const void *saddr,
643     size_t saddrsize)
644 {
645 	void *cookie;
646 	const void *limit;
647 	size_t limitsize;
648 
649 	cookie = NULL;
650 	while (nvlist_next(salimits, NULL, &cookie) != NULL) {
651 		limit = cnvlist_get_binary(cookie, &limitsize);
652 
653 		if (limitsize != saddrsize) {
654 			continue;
655 		}
656 		if (memcmp(limit, saddr, limitsize) == 0) {
657 			return (true);
658 		}
659 
660 		/*
661 		 * In case of deprecated version (gethostbyname) we have to
662 		 * ignore port, because there is no such info in the hostent.
663 		 * Suporting only AF_INET and AF_INET6.
664 		 */
665 		if (strcmp(cnvlist_name(cookie), "d") != 0 ||
666 		    (saddrsize != sizeof(struct sockaddr_in) &&
667 		    saddrsize != sizeof(struct sockaddr_in6))) {
668 			continue;
669 		}
670 		if (saddrsize == sizeof(struct sockaddr_in)) {
671 			const struct sockaddr_in *saddrptr;
672 			struct sockaddr_in sockaddr;
673 
674 			saddrptr = (const struct sockaddr_in *)saddr;
675 			memcpy(&sockaddr, limit, sizeof(sockaddr));
676 			sockaddr.sin_port = saddrptr->sin_port;
677 
678 			if (memcmp(&sockaddr, saddr, saddrsize) == 0) {
679 				return (true);
680 			}
681 		} else if (saddrsize == sizeof(struct sockaddr_in6)) {
682 			const struct sockaddr_in6 *saddrptr;
683 			struct sockaddr_in6 sockaddr;
684 
685 			saddrptr = (const struct sockaddr_in6 *)saddr;
686 			memcpy(&sockaddr, limit, sizeof(sockaddr));
687 			sockaddr.sin6_port = saddrptr->sin6_port;
688 
689 			if (memcmp(&sockaddr, saddr, saddrsize) == 0) {
690 				return (true);
691 			}
692 		}
693 	}
694 
695 	return (false);
696 }
697 
698 static bool
net_allowed_bsaddr(const nvlist_t * limits,const void * saddr,size_t saddrsize)699 net_allowed_bsaddr(const nvlist_t *limits, const void *saddr, size_t saddrsize)
700 {
701 
702 	if (limits == NULL)
703 		return (true);
704 
705 	if (!nvlist_exists_nvlist(limits, "sockaddr"))
706 		return (true);
707 
708 	return (net_allowed_bsaddr_impl(nvlist_get_nvlist(limits, "sockaddr"),
709 	    saddr, saddrsize));
710 }
711 
712 static bool
net_allowed_hosts(const nvlist_t * limits,const char * name,const char * srvname)713 net_allowed_hosts(const nvlist_t *limits, const char *name, const char *srvname)
714 {
715 	void *cookie;
716 	const nvlist_t *hlimits;
717 	const char *testname, *testsrvname;
718 
719 	if (limits == NULL) {
720 		return (true);
721 	}
722 
723 	/* If there are no hosts at all, allow any. */
724 	if (!nvlist_exists_nvlist(limits, "hosts")) {
725 		return (true);
726 	}
727 
728 	cookie = NULL;
729 	testname = (name == NULL ? "" : name);
730 	testsrvname = (srvname == NULL ? "" : srvname);
731 	hlimits = nvlist_get_nvlist(limits, "hosts");
732 	while (nvlist_next(hlimits, NULL, &cookie) != NULL) {
733 		if (strcmp(cnvlist_name(cookie), "") != 0 &&
734 		    strcmp(cnvlist_name(cookie), testname) != 0) {
735 			continue;
736 		}
737 
738 		if (strcmp(cnvlist_get_string(cookie), "") != 0 &&
739 		    strcmp(cnvlist_get_string(cookie), testsrvname) != 0) {
740 			continue;
741 		}
742 
743 		return (true);
744 	}
745 
746 	return (false);
747 }
748 
749 static void
hostent_pack(const struct hostent * hp,nvlist_t * nvl,bool addtocache)750 hostent_pack(const struct hostent *hp, nvlist_t *nvl, bool addtocache)
751 {
752 	unsigned int ii;
753 	char nvlname[64];
754 	int n;
755 
756 	nvlist_add_string(nvl, "name", hp->h_name);
757 	nvlist_add_number(nvl, "addrtype", (uint64_t)hp->h_addrtype);
758 	nvlist_add_number(nvl, "length", (uint64_t)hp->h_length);
759 
760 	if (hp->h_aliases == NULL) {
761 		nvlist_add_number(nvl, "naliases", 0);
762 	} else {
763 		for (ii = 0; hp->h_aliases[ii] != NULL; ii++) {
764 			n = snprintf(nvlname, sizeof(nvlname), "alias%u", ii);
765 			assert(n > 0 && n < (int)sizeof(nvlname));
766 			nvlist_add_string(nvl, nvlname, hp->h_aliases[ii]);
767 		}
768 		nvlist_add_number(nvl, "naliases", (uint64_t)ii);
769 	}
770 
771 	if (hp->h_addr_list == NULL) {
772 		nvlist_add_number(nvl, "naddrs", 0);
773 	} else {
774 		for (ii = 0; hp->h_addr_list[ii] != NULL; ii++) {
775 			n = snprintf(nvlname, sizeof(nvlname), "addr%u", ii);
776 			assert(n > 0 && n < (int)sizeof(nvlname));
777 			nvlist_add_binary(nvl, nvlname, hp->h_addr_list[ii],
778 			    (size_t)hp->h_length);
779 			if (addtocache) {
780 				net_add_hostent_to_cache(hp->h_addr_list[ii],
781 				    hp->h_length, hp->h_addrtype);
782 			}
783 		}
784 		nvlist_add_number(nvl, "naddrs", (uint64_t)ii);
785 	}
786 }
787 
788 static int
net_gethostbyname(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)789 net_gethostbyname(const nvlist_t *limits, const nvlist_t *nvlin,
790     nvlist_t *nvlout)
791 {
792 	struct hostent *hp;
793 	int family;
794 	const nvlist_t *funclimit;
795 	const char *name;
796 	bool dnscache;
797 
798 	if (!net_allowed_mode(limits, CAPNET_DEPRECATED_NAME2ADDR))
799 		return (ENOTCAPABLE);
800 
801 	dnscache = net_allowed_mode(limits, CAPNET_CONNECTDNS);
802 	funclimit = NULL;
803 	if (limits != NULL) {
804 		funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_NAME2ADDR,
805 		    NULL);
806 	}
807 
808 	family = (int)nvlist_get_number(nvlin, "family");
809 	if (!net_allowed_family(funclimit, family))
810 		return (ENOTCAPABLE);
811 
812 	name = nvlist_get_string(nvlin, "name");
813 	if (!net_allowed_hosts(funclimit, name, ""))
814 		return (ENOTCAPABLE);
815 
816 	hp = gethostbyname2(name, family);
817 	if (hp == NULL)
818 		return (h_errno);
819 	hostent_pack(hp, nvlout, dnscache);
820 	return (0);
821 }
822 
823 static int
net_gethostbyaddr(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)824 net_gethostbyaddr(const nvlist_t *limits, const nvlist_t *nvlin,
825     nvlist_t *nvlout)
826 {
827 	struct hostent *hp;
828 	const void *addr;
829 	size_t addrsize;
830 	int family;
831 	const nvlist_t *funclimit;
832 
833 	if (!net_allowed_mode(limits, CAPNET_DEPRECATED_ADDR2NAME))
834 		return (ENOTCAPABLE);
835 
836 	funclimit = NULL;
837 	if (limits != NULL) {
838 		funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_ADDR2NAME,
839 		    NULL);
840 	}
841 
842 	family = (int)nvlist_get_number(nvlin, "family");
843 	if (!net_allowed_family(funclimit, family))
844 		return (ENOTCAPABLE);
845 
846 	addr = nvlist_get_binary(nvlin, "addr", &addrsize);
847 	if (!net_allowed_bsaddr(funclimit, addr, addrsize))
848 		return (ENOTCAPABLE);
849 
850 	hp = gethostbyaddr(addr, (socklen_t)addrsize, family);
851 	if (hp == NULL)
852 		return (h_errno);
853 	hostent_pack(hp, nvlout, false);
854 	return (0);
855 }
856 
857 static int
net_getnameinfo(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)858 net_getnameinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
859 {
860 	struct sockaddr_storage sast;
861 	const void *sabin;
862 	char *host, *serv;
863 	size_t sabinsize, hostlen, servlen;
864 	socklen_t salen;
865 	int error, serrno, flags;
866 	const nvlist_t *funclimit;
867 
868 	host = serv = NULL;
869 	if (!net_allowed_mode(limits, CAPNET_ADDR2NAME)) {
870 		serrno = ENOTCAPABLE;
871 		error = EAI_SYSTEM;
872 		goto out;
873 	}
874 	funclimit = NULL;
875 	if (limits != NULL) {
876 		funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_ADDR2NAME,
877 		    NULL);
878 	}
879 	error = 0;
880 	memset(&sast, 0, sizeof(sast));
881 
882 	hostlen = (size_t)nvlist_get_number(nvlin, "hostlen");
883 	servlen = (size_t)nvlist_get_number(nvlin, "servlen");
884 
885 	if (hostlen > 0) {
886 		host = calloc(1, hostlen);
887 		if (host == NULL) {
888 			error = EAI_MEMORY;
889 			goto out;
890 		}
891 	}
892 	if (servlen > 0) {
893 		serv = calloc(1, servlen);
894 		if (serv == NULL) {
895 			error = EAI_MEMORY;
896 			goto out;
897 		}
898 	}
899 
900 	sabin = nvlist_get_binary(nvlin, "sa", &sabinsize);
901 	if (sabinsize > sizeof(sast)) {
902 		error = EAI_FAIL;
903 		goto out;
904 	}
905 	if (!net_allowed_bsaddr(funclimit, sabin, sabinsize)) {
906 		serrno = ENOTCAPABLE;
907 		error = EAI_SYSTEM;
908 		goto out;
909 	}
910 
911 	memcpy(&sast, sabin, sabinsize);
912 	salen = (socklen_t)sabinsize;
913 
914 	if ((sast.ss_family != AF_INET ||
915 	     salen != sizeof(struct sockaddr_in)) &&
916 	    (sast.ss_family != AF_INET6 ||
917 	     salen != sizeof(struct sockaddr_in6))) {
918 		error = EAI_FAIL;
919 		goto out;
920 	}
921 
922 	if (!net_allowed_family(funclimit, (int)sast.ss_family)) {
923 		serrno = ENOTCAPABLE;
924 		error = EAI_SYSTEM;
925 		goto out;
926 	}
927 
928 	flags = (int)nvlist_get_number(nvlin, "flags");
929 
930 	error = getnameinfo((struct sockaddr *)&sast, salen, host, hostlen,
931 	    serv, servlen, flags);
932 	serrno = errno;
933 	if (error != 0)
934 		goto out;
935 
936 	if (host != NULL)
937 		nvlist_move_string(nvlout, "host", host);
938 	if (serv != NULL)
939 		nvlist_move_string(nvlout, "serv", serv);
940 out:
941 	if (error != 0) {
942 		free(host);
943 		free(serv);
944 		if (error == EAI_SYSTEM)
945 			nvlist_add_number(nvlout, "errno", serrno);
946 	}
947 	return (error);
948 }
949 
950 static nvlist_t *
addrinfo_pack(const struct addrinfo * ai)951 addrinfo_pack(const struct addrinfo *ai)
952 {
953 	nvlist_t *nvl;
954 
955 	nvl = nvlist_create(0);
956 	nvlist_add_number(nvl, "ai_flags", (uint64_t)ai->ai_flags);
957 	nvlist_add_number(nvl, "ai_family", (uint64_t)ai->ai_family);
958 	nvlist_add_number(nvl, "ai_socktype", (uint64_t)ai->ai_socktype);
959 	nvlist_add_number(nvl, "ai_protocol", (uint64_t)ai->ai_protocol);
960 	nvlist_add_binary(nvl, "ai_addr", ai->ai_addr, (size_t)ai->ai_addrlen);
961 	if (ai->ai_canonname != NULL)
962 		nvlist_add_string(nvl, "ai_canonname", ai->ai_canonname);
963 
964 	return (nvl);
965 }
966 
967 static int
net_getaddrinfo(const nvlist_t * limits,const nvlist_t * nvlin,nvlist_t * nvlout)968 net_getaddrinfo(const nvlist_t *limits, const nvlist_t *nvlin, nvlist_t *nvlout)
969 {
970 	struct addrinfo hints, *hintsp, *res, *cur;
971 	const char *hostname, *servname;
972 	char nvlname[64];
973 	nvlist_t *elem;
974 	unsigned int ii;
975 	int error, serrno, family, n;
976 	const nvlist_t *funclimit;
977 	bool dnscache;
978 
979 	if (!net_allowed_mode(limits, CAPNET_NAME2ADDR)) {
980 		serrno = ENOTCAPABLE;
981 		error = EAI_SYSTEM;
982 		goto out;
983 	}
984 	dnscache = net_allowed_mode(limits, CAPNET_CONNECTDNS);
985 	funclimit = NULL;
986 	if (limits != NULL) {
987 		funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_NAME2ADDR,
988 		    NULL);
989 	}
990 
991 	hostname = dnvlist_get_string(nvlin, "hostname", NULL);
992 	servname = dnvlist_get_string(nvlin, "servname", NULL);
993 	if (nvlist_exists_number(nvlin, "hints.ai_flags")) {
994 		hints.ai_flags = (int)nvlist_get_number(nvlin,
995 		    "hints.ai_flags");
996 		hints.ai_family = (int)nvlist_get_number(nvlin,
997 		    "hints.ai_family");
998 		hints.ai_socktype = (int)nvlist_get_number(nvlin,
999 		    "hints.ai_socktype");
1000 		hints.ai_protocol = (int)nvlist_get_number(nvlin,
1001 		    "hints.ai_protocol");
1002 		hints.ai_addrlen = 0;
1003 		hints.ai_addr = NULL;
1004 		hints.ai_canonname = NULL;
1005 		hints.ai_next = NULL;
1006 		hintsp = &hints;
1007 		family = hints.ai_family;
1008 	} else {
1009 		hintsp = NULL;
1010 		family = AF_UNSPEC;
1011 	}
1012 
1013 	if (!net_allowed_family(funclimit, family)) {
1014 		errno = ENOTCAPABLE;
1015 		error = EAI_SYSTEM;
1016 		goto out;
1017 	}
1018 	if (!net_allowed_hosts(funclimit, hostname, servname)) {
1019 		errno = ENOTCAPABLE;
1020 		error = EAI_SYSTEM;
1021 		goto out;
1022 	}
1023 	error = getaddrinfo(hostname, servname, hintsp, &res);
1024 	serrno = errno;
1025 	if (error != 0) {
1026 		goto out;
1027 	}
1028 
1029 	for (cur = res, ii = 0; cur != NULL; cur = cur->ai_next, ii++) {
1030 		elem = addrinfo_pack(cur);
1031 		n = snprintf(nvlname, sizeof(nvlname), "res%u", ii);
1032 		assert(n > 0 && n < (int)sizeof(nvlname));
1033 		nvlist_move_nvlist(nvlout, nvlname, elem);
1034 		if (dnscache) {
1035 			net_add_sockaddr_to_cache(cur->ai_addr,
1036 			    cur->ai_addrlen, false);
1037 		}
1038 	}
1039 
1040 	freeaddrinfo(res);
1041 	error = 0;
1042 out:
1043 	if (error == EAI_SYSTEM)
1044 		nvlist_add_number(nvlout, "errno", serrno);
1045 	return (error);
1046 }
1047 
1048 static int
net_bind(const nvlist_t * limits,nvlist_t * nvlin,nvlist_t * nvlout)1049 net_bind(const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout)
1050 {
1051 	int socket, serrno;
1052 	const void *saddr;
1053 	size_t len;
1054 	const nvlist_t *funclimit;
1055 
1056 	if (!net_allowed_mode(limits, CAPNET_BIND))
1057 		return (ENOTCAPABLE);
1058 	funclimit = NULL;
1059 	if (limits != NULL)
1060 		funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_BIND, NULL);
1061 
1062 	saddr = nvlist_get_binary(nvlin, "saddr", &len);
1063 
1064 	if (!net_allowed_bsaddr(funclimit, saddr, len))
1065 		return (ENOTCAPABLE);
1066 
1067 	socket = nvlist_take_descriptor(nvlin, "s");
1068 	if (bind(socket, saddr, len) < 0) {
1069 		serrno = errno;
1070 		close(socket);
1071 		return (serrno);
1072 	}
1073 
1074 	nvlist_move_descriptor(nvlout, "s", socket);
1075 
1076 	return (0);
1077 }
1078 
1079 static int
net_connect(const nvlist_t * limits,nvlist_t * nvlin,nvlist_t * nvlout)1080 net_connect(const nvlist_t *limits, nvlist_t *nvlin, nvlist_t *nvlout)
1081 {
1082 	int socket, serrno;
1083 	const void *saddr;
1084 	const nvlist_t *funclimit;
1085 	size_t len;
1086 	bool conn, conndns, allowed;
1087 
1088 	conn = net_allowed_mode(limits, CAPNET_CONNECT);
1089 	conndns = net_allowed_mode(limits, CAPNET_CONNECTDNS);
1090 
1091 	if (!conn && !conndns)
1092 		return (ENOTCAPABLE);
1093 
1094 	funclimit = NULL;
1095 	if (limits != NULL)
1096 		funclimit = dnvlist_get_nvlist(limits, LIMIT_NV_CONNECT, NULL);
1097 
1098 	saddr = nvlist_get_binary(nvlin, "saddr", &len);
1099 	allowed = false;
1100 
1101 	if (conn && net_allowed_bsaddr(funclimit, saddr, len)) {
1102 		allowed = true;
1103 	}
1104 	if (conndns && capdnscache != NULL &&
1105 	   net_allowed_bsaddr_impl(capdnscache, saddr, len)) {
1106 		allowed = true;
1107 	}
1108 
1109 	if (allowed == false) {
1110 		return (ENOTCAPABLE);
1111 	}
1112 
1113 	socket = dup(nvlist_get_descriptor(nvlin, "s"));
1114 	if (connect(socket, saddr, len) < 0) {
1115 		serrno = errno;
1116 		close(socket);
1117 		return (serrno);
1118 	}
1119 
1120 	nvlist_move_descriptor(nvlout, "s", socket);
1121 
1122 	return (0);
1123 }
1124 
1125 static bool
verify_only_sa_newlimts(const nvlist_t * oldfunclimits,const nvlist_t * newfunclimit)1126 verify_only_sa_newlimts(const nvlist_t *oldfunclimits,
1127     const nvlist_t *newfunclimit)
1128 {
1129 	void *cookie;
1130 
1131 	cookie = NULL;
1132 	while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) {
1133 		void *sacookie;
1134 
1135 		if (strcmp(cnvlist_name(cookie), "sockaddr") != 0)
1136 			return (false);
1137 
1138 		if (cnvlist_type(cookie) != NV_TYPE_NVLIST)
1139 			return (false);
1140 
1141 		sacookie = NULL;
1142 		while (nvlist_next(cnvlist_get_nvlist(cookie), NULL,
1143 		    &sacookie) != NULL) {
1144 			const void *sa;
1145 			size_t sasize;
1146 
1147 			if (cnvlist_type(sacookie) != NV_TYPE_BINARY)
1148 				return (false);
1149 
1150 			sa = cnvlist_get_binary(sacookie, &sasize);
1151 			if (!net_allowed_bsaddr(oldfunclimits, sa, sasize))
1152 				return (false);
1153 		}
1154 	}
1155 
1156 	return (true);
1157 }
1158 
1159 static bool
verify_bind_newlimts(const nvlist_t * oldlimits,const nvlist_t * newfunclimit)1160 verify_bind_newlimts(const nvlist_t *oldlimits,
1161     const nvlist_t *newfunclimit)
1162 {
1163 	const nvlist_t *oldfunclimits;
1164 
1165 	oldfunclimits = NULL;
1166 	if (oldlimits != NULL) {
1167 		oldfunclimits = dnvlist_get_nvlist(oldlimits, LIMIT_NV_BIND,
1168 		    NULL);
1169 	}
1170 
1171 	return (verify_only_sa_newlimts(oldfunclimits, newfunclimit));
1172 }
1173 
1174 
1175 static bool
verify_connect_newlimits(const nvlist_t * oldlimits,const nvlist_t * newfunclimit)1176 verify_connect_newlimits(const nvlist_t *oldlimits,
1177     const nvlist_t *newfunclimit)
1178 {
1179 	const nvlist_t *oldfunclimits;
1180 
1181 	oldfunclimits = NULL;
1182 	if (oldlimits != NULL) {
1183 		oldfunclimits = dnvlist_get_nvlist(oldlimits, LIMIT_NV_CONNECT,
1184 		    NULL);
1185 	}
1186 
1187 	return (verify_only_sa_newlimts(oldfunclimits, newfunclimit));
1188 }
1189 
1190 static bool
verify_addr2name_newlimits(const nvlist_t * oldlimits,const nvlist_t * newfunclimit)1191 verify_addr2name_newlimits(const nvlist_t *oldlimits,
1192     const nvlist_t *newfunclimit)
1193 {
1194 	void *cookie;
1195 	const nvlist_t *oldfunclimits;
1196 
1197 	oldfunclimits = NULL;
1198 	if (oldlimits != NULL) {
1199 		oldfunclimits = dnvlist_get_nvlist(oldlimits,
1200 		    LIMIT_NV_ADDR2NAME, NULL);
1201 	}
1202 
1203 	cookie = NULL;
1204 	while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) {
1205 		if (strcmp(cnvlist_name(cookie), "sockaddr") == 0) {
1206 			void *sacookie;
1207 
1208 			if (cnvlist_type(cookie) != NV_TYPE_NVLIST)
1209 				return (false);
1210 
1211 			sacookie = NULL;
1212 			while (nvlist_next(cnvlist_get_nvlist(cookie), NULL,
1213 			    &sacookie) != NULL) {
1214 				const void *sa;
1215 				size_t sasize;
1216 
1217 				if (cnvlist_type(sacookie) != NV_TYPE_BINARY)
1218 					return (false);
1219 
1220 				sa = cnvlist_get_binary(sacookie, &sasize);
1221 				if (!net_allowed_bsaddr(oldfunclimits, sa,
1222 				    sasize)) {
1223 					return (false);
1224 				}
1225 			}
1226 		} else if (strcmp(cnvlist_name(cookie), "family") == 0) {
1227 			size_t i, sfamilies;
1228 			const uint64_t *families;
1229 
1230 			if (cnvlist_type(cookie) != NV_TYPE_NUMBER_ARRAY)
1231 				return (false);
1232 
1233 			families = cnvlist_get_number_array(cookie, &sfamilies);
1234 			for (i = 0; i < sfamilies; i++) {
1235 				if (!net_allowed_family(oldfunclimits,
1236 				    families[i])) {
1237 					return (false);
1238 				}
1239 			}
1240 		} else {
1241 			return (false);
1242 		}
1243 	}
1244 
1245 	return (true);
1246 }
1247 
1248 static bool
verify_name2addr_newlimits(const nvlist_t * oldlimits,const nvlist_t * newfunclimit)1249 verify_name2addr_newlimits(const nvlist_t *oldlimits,
1250     const nvlist_t *newfunclimit)
1251 {
1252 	void *cookie;
1253 	const nvlist_t *oldfunclimits;
1254 
1255 	oldfunclimits = NULL;
1256 	if (oldlimits != NULL) {
1257 		oldfunclimits = dnvlist_get_nvlist(oldlimits,
1258 		    LIMIT_NV_NAME2ADDR, NULL);
1259 	}
1260 
1261 	cookie = NULL;
1262 	while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) {
1263 		if (strcmp(cnvlist_name(cookie), "hosts") == 0) {
1264 			void *hostcookie;
1265 
1266 			if (cnvlist_type(cookie) != NV_TYPE_NVLIST)
1267 				return (false);
1268 
1269 			hostcookie = NULL;
1270 			while (nvlist_next(cnvlist_get_nvlist(cookie), NULL,
1271 			    &hostcookie) != NULL) {
1272 				if (cnvlist_type(hostcookie) != NV_TYPE_STRING)
1273 					return (false);
1274 
1275 				if (!net_allowed_hosts(oldfunclimits,
1276 				    cnvlist_name(hostcookie),
1277 				    cnvlist_get_string(hostcookie))) {
1278 					return (false);
1279 				}
1280 			}
1281 		} else if (strcmp(cnvlist_name(cookie), "family") == 0) {
1282 			size_t i, sfamilies;
1283 			const uint64_t *families;
1284 
1285 			if (cnvlist_type(cookie) != NV_TYPE_NUMBER_ARRAY)
1286 				return (false);
1287 
1288 			families = cnvlist_get_number_array(cookie, &sfamilies);
1289 			for (i = 0; i < sfamilies; i++) {
1290 				if (!net_allowed_family(oldfunclimits,
1291 				    families[i])) {
1292 					return (false);
1293 				}
1294 			}
1295 		} else {
1296 			return (false);
1297 		}
1298 	}
1299 
1300 	return (true);
1301 }
1302 
1303 static int
net_limit(const nvlist_t * oldlimits,const nvlist_t * newlimits)1304 net_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
1305 {
1306 	const char *name;
1307 	void *cookie;
1308 	bool hasmode, hasconnect, hasbind, hasaddr2name, hasname2addr;
1309 
1310 	/*
1311 	 * Modes:
1312 	 *	ADDR2NAME:
1313 	 *		getnameinfo
1314 	 *	DEPRECATED_ADDR2NAME:
1315 	 *		gethostbyaddr
1316 	 *
1317 	 *	NAME2ADDR:
1318 	 *		getaddrinfo
1319 	 *	DEPRECATED_NAME2ADDR:
1320 	 *		gethostbyname
1321 	 *
1322 	 * Limit scheme:
1323 	 *	mode	: NV_TYPE_NUMBER
1324 	 *	connect : NV_TYPE_NVLIST
1325 	 *		sockaddr : NV_TYPE_NVLIST
1326 	 *			""	: NV_TYPE_BINARY
1327 	 *			...	: NV_TYPE_BINARY
1328 	 *	bind	: NV_TYPE_NVLIST
1329 	 *		sockaddr : NV_TYPE_NVLIST
1330 	 *			""	: NV_TYPE_BINARY
1331 	 *			...	: NV_TYPE_BINARY
1332 	 *	addr2name : NV_TYPE_NVLIST
1333 	 *		family  : NV_TYPE_NUMBER_ARRAY
1334 	 *		sockaddr : NV_TYPE_NVLIST
1335 	 *			""	: NV_TYPE_BINARY
1336 	 *			...	: NV_TYPE_BINARY
1337 	 *	name2addr : NV_TYPE_NVLIST
1338 	 *		family : NV_TYPE_NUMBER
1339 	 *		hosts	: NV_TYPE_NVLIST
1340 	 *			host	: servname : NV_TYPE_STRING
1341 	 */
1342 
1343 	hasmode = false;
1344 	hasconnect = false;
1345 	hasbind = false;
1346 	hasaddr2name = false;
1347 	hasname2addr = false;
1348 
1349 	cookie = NULL;
1350 	while ((name = nvlist_next(newlimits, NULL, &cookie)) != NULL) {
1351 		if (strcmp(name, "mode") == 0) {
1352 			if (cnvlist_type(cookie) != NV_TYPE_NUMBER) {
1353 				return (NO_RECOVERY);
1354 			}
1355 			if (!net_allowed_mode(oldlimits,
1356 			    cnvlist_get_number(cookie))) {
1357 				return (ENOTCAPABLE);
1358 			}
1359 			hasmode = true;
1360 			continue;
1361 		}
1362 
1363 		if (cnvlist_type(cookie) != NV_TYPE_NVLIST) {
1364 			return (NO_RECOVERY);
1365 		}
1366 
1367 		if (strcmp(name, LIMIT_NV_BIND) == 0) {
1368 			hasbind = true;
1369 			if (!verify_bind_newlimts(oldlimits,
1370 			    cnvlist_get_nvlist(cookie))) {
1371 				return (ENOTCAPABLE);
1372 			}
1373 		} else if (strcmp(name, LIMIT_NV_CONNECT) == 0) {
1374 			hasconnect = true;
1375 			if (!verify_connect_newlimits(oldlimits,
1376 			    cnvlist_get_nvlist(cookie))) {
1377 				return (ENOTCAPABLE);
1378 			}
1379 		} else if (strcmp(name, LIMIT_NV_ADDR2NAME) == 0) {
1380 			hasaddr2name = true;
1381 			if (!verify_addr2name_newlimits(oldlimits,
1382 			    cnvlist_get_nvlist(cookie))) {
1383 				return (ENOTCAPABLE);
1384 			}
1385 		} else if (strcmp(name, LIMIT_NV_NAME2ADDR) == 0) {
1386 			hasname2addr = true;
1387 			if (!verify_name2addr_newlimits(oldlimits,
1388 			    cnvlist_get_nvlist(cookie))) {
1389 				return (ENOTCAPABLE);
1390 			}
1391 		}
1392 	}
1393 
1394 	/* Mode is required. */
1395 	if (!hasmode)
1396 		return (ENOTCAPABLE);
1397 
1398 	/*
1399 	 * If the new limit doesn't mention mode or family we have to
1400 	 * check if the current limit does have those. Missing mode or
1401 	 * family in the limit means that all modes or families are
1402 	 * allowed.
1403 	 */
1404 	if (oldlimits == NULL)
1405 		return (0);
1406 	if (!hasbind && nvlist_exists(oldlimits, LIMIT_NV_BIND))
1407 		return (ENOTCAPABLE);
1408 	if (!hasconnect && nvlist_exists(oldlimits, LIMIT_NV_CONNECT))
1409 		return (ENOTCAPABLE);
1410 	if (!hasaddr2name && nvlist_exists(oldlimits, LIMIT_NV_ADDR2NAME))
1411 		return (ENOTCAPABLE);
1412 	if (!hasname2addr && nvlist_exists(oldlimits, LIMIT_NV_NAME2ADDR))
1413 		return (ENOTCAPABLE);
1414 	return (0);
1415 }
1416 
1417 static int
net_command(const char * cmd,const nvlist_t * limits,nvlist_t * nvlin,nvlist_t * nvlout)1418 net_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
1419     nvlist_t *nvlout)
1420 {
1421 
1422 	if (strcmp(cmd, "bind") == 0)
1423 		return (net_bind(limits, nvlin, nvlout));
1424 	else if (strcmp(cmd, "connect") == 0)
1425 		return (net_connect(limits, nvlin, nvlout));
1426 	else if (strcmp(cmd, "gethostbyname") == 0)
1427 		return (net_gethostbyname(limits, nvlin, nvlout));
1428 	else if (strcmp(cmd, "gethostbyaddr") == 0)
1429 		return (net_gethostbyaddr(limits, nvlin, nvlout));
1430 	else if (strcmp(cmd, "getnameinfo") == 0)
1431 		return (net_getnameinfo(limits, nvlin, nvlout));
1432 	else if (strcmp(cmd, "getaddrinfo") == 0)
1433 		return (net_getaddrinfo(limits, nvlin, nvlout));
1434 
1435 	return (EINVAL);
1436 }
1437 
1438 CREATE_SERVICE("system.net", net_limit, net_command, 0);
1439