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