portalgo.c revision 1.9 1 /* $NetBSD: portalgo.c,v 1.9 2015/08/24 22:21:26 pooka Exp $ */
2
3 /*
4 * Copyright 2011 Vlad Balan
5 *
6 * Written by Vlad Balan for the NetBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 */
30
31 /*
32 * see:
33 * RFC 6056 Recommendations for Transport-Protocol Port Randomization
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: portalgo.c,v 1.9 2015/08/24 22:21:26 pooka Exp $");
38
39 #ifdef _KERNEL_OPT
40 #include "opt_inet.h"
41 #endif
42
43 #include <sys/param.h>
44 #include <sys/errno.h>
45 #include <sys/kauth.h>
46 #include <sys/uidinfo.h>
47 #include <sys/domain.h>
48 #include <sys/md5.h>
49 #include <sys/cprng.h>
50 #include <sys/bitops.h>
51
52 #include <net/if.h>
53 #include <net/route.h>
54
55 #include <netinet/in.h>
56 #include <netinet/in_systm.h>
57 #include <netinet/ip.h>
58 #include <netinet/in_pcb.h>
59 #include <netinet/in_var.h>
60 #include <netinet/ip_var.h>
61
62 #ifdef INET6
63 #include <netinet/ip6.h>
64 #include <netinet6/ip6_var.h>
65 #include <netinet6/in6_pcb.h>
66 #endif
67
68 #include <netinet/tcp_vtw.h>
69
70 #include "portalgo.h"
71
72 #define NPROTO 2
73 #define PORTALGO_TCP 0
74 #define PORTALGO_UDP 1
75
76 #define NAF 2
77 #define PORTALGO_IPV4 0
78 #define PORTALGO_IPV6 1
79
80 #define NRANGES 2
81 #define PORTALGO_LOWPORT 0
82 #define PORTALGO_HIGHPORT 1
83
84 #if PORTALGO_DEBUG
85 static bool portalgo_debug = true;
86 #define DPRINTF if (portalgo_debug) printf
87 #else
88 #define DPRINTF while (/*CONSTCOND*/0) printf
89 #endif
90
91 #ifndef PORTALGO_INET4_DEFAULT
92 #define PORTALGO_INET4_DEFAULT PORTALGO_BSD
93 #endif
94 #ifndef PORTALGO_INET6_DEFAULT
95 #define PORTALGO_INET6_DEFAULT PORTALGO_BSD
96 #endif
97
98 typedef __BITMAP_TYPE(, uint32_t, 0x10000) bitmap;
99 #ifdef INET
100 static int inet4_portalgo = PORTALGO_INET4_DEFAULT;
101 static bitmap inet4_reserve;
102 #endif
103 #ifdef INET6
104 static int inet6_portalgo = PORTALGO_INET6_DEFAULT;
105 static bitmap inet6_reserve;
106 #endif
107
108 typedef struct {
109 const char *name;
110 int (*func)(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
111 } portalgo_algorithm_t;
112
113 static int algo_bsd(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
114 static int algo_random_start(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
115 static int algo_random_pick(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
116 static int algo_hash(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
117 static int algo_doublehash(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
118 static int algo_randinc(int, uint16_t *, struct inpcb_hdr *, kauth_cred_t);
119
120 static const portalgo_algorithm_t algos[] = {
121 {
122 .name = "bsd",
123 .func = algo_bsd
124 },
125 {
126 .name = "random_start",
127 .func = algo_random_start
128 },
129 {
130 .name = "random_pick",
131 .func = algo_random_pick
132 },
133 {
134 .name = "hash",
135 .func = algo_hash
136 },
137 {
138 .name = "doublehash",
139 .func = algo_doublehash
140 },
141 {
142 .name = "randinc",
143 .func = algo_randinc
144 }
145 };
146
147 #define NALGOS __arraycount(algos)
148
149 static uint16_t portalgo_next_ephemeral[NPROTO][NAF][NRANGES][NALGOS];
150
151 /*
152 * Access the pcb and copy the values of the last port and the ends of
153 * the port range.
154 */
155 static int
156 pcb_getports(struct inpcb_hdr *inp_hdr, uint16_t *lastport,
157 uint16_t *mymin, uint16_t *mymax, uint16_t **pnext_ephemeral, int algo)
158 {
159 struct inpcbtable * const table = inp_hdr->inph_table;
160 struct socket *so;
161 int portalgo_proto;
162 int portalgo_af;
163 int portalgo_range;
164
165 so = inp_hdr->inph_socket;
166 switch (so->so_type) {
167 case SOCK_DGRAM: /* UDP or DCCP */
168 case SOCK_CONN_DGRAM:
169 portalgo_proto = PORTALGO_UDP;
170 break;
171 case SOCK_STREAM: /* TCP or SCTP */
172 portalgo_proto = PORTALGO_TCP;
173 break;
174 default:
175 return EPFNOSUPPORT;
176 }
177
178 switch (inp_hdr->inph_af) {
179 #ifdef INET
180 case AF_INET: {
181 struct inpcb *inp = (struct inpcb *)(void *)inp_hdr;
182
183 portalgo_af = PORTALGO_IPV4;
184 if (inp->inp_flags & INP_LOWPORT) {
185 *mymin = lowportmin;
186 *mymax = lowportmax;
187 *lastport = table->inpt_lastlow;
188 portalgo_range = PORTALGO_LOWPORT;
189 } else {
190 *mymin = anonportmin;
191 *mymax = anonportmax;
192 *lastport = table->inpt_lastport;
193 portalgo_range = PORTALGO_HIGHPORT;
194 }
195 break;
196 }
197 #endif
198 #ifdef INET6
199 case AF_INET6: {
200 struct in6pcb *in6p = (struct in6pcb *)(void *)inp_hdr;
201
202 portalgo_af = PORTALGO_IPV6;
203 if (in6p->in6p_flags & IN6P_LOWPORT) {
204 *mymin = ip6_lowportmin;
205 *mymax = ip6_lowportmax;
206 *lastport = table->inpt_lastlow;
207 portalgo_range = PORTALGO_LOWPORT;
208 } else {
209 *mymin = ip6_anonportmin;
210 *mymax = ip6_anonportmax;
211 *lastport = table->inpt_lastport;
212 portalgo_range = PORTALGO_HIGHPORT;
213 }
214 break;
215 }
216 #endif
217 default:
218 return EAFNOSUPPORT;
219 }
220
221 if (*mymin > *mymax) { /* sanity check */
222 u_int16_t swp;
223
224 swp = *mymin;
225 *mymin = *mymax;
226 *mymax = swp;
227 }
228
229 DPRINTF("%s mymin:%d mymax:%d lastport:%d\n", __func__,
230 *mymin, *mymax, *lastport);
231
232 *pnext_ephemeral = &portalgo_next_ephemeral[portalgo_proto]
233 [portalgo_af][portalgo_range][algo];
234
235 DPRINTF("%s portalgo_proto:%d portalgo_af:%d portalgo_range:%d\n",
236 __func__, portalgo_proto, portalgo_af, portalgo_range);
237 return 0;
238 }
239
240 /*
241 * Check whether the port picked by the port randomizer is available
242 * and whether KAUTH approves of our choice. This part of the code
243 * shamelessly copied from in_pcb.c.
244 */
245 static bool
246 check_suitable_port(uint16_t port, struct inpcb_hdr *inp_hdr, kauth_cred_t cred)
247 {
248 struct inpcbtable * const table = inp_hdr->inph_table;
249 #ifdef INET
250 vestigial_inpcb_t vestigial;
251 #endif
252 int error;
253 #ifdef INET6
254 struct socket *so;
255 int wild = 0;
256 #endif
257
258 DPRINTF("%s called for argument %d\n", __func__, port);
259
260 switch (inp_hdr->inph_af) {
261 #ifdef INET
262 case AF_INET: { /* IPv4 */
263 struct inpcb *inp = (struct inpcb *)(void *)inp_hdr;
264 struct inpcb *pcb;
265 struct sockaddr_in sin;
266
267 if (__BITMAP_ISSET(port, &inet4_reserve))
268 return false;
269
270 sin.sin_addr = inp->inp_laddr;
271 pcb = in_pcblookup_port(table, sin.sin_addr, htons(port), 1,
272 &vestigial);
273
274 DPRINTF("%s in_pcblookup_port returned %p and "
275 "vestigial.valid %d\n",
276 __func__, pcb, vestigial.valid);
277
278 if ((!pcb) && (!vestigial.valid)) {
279 enum kauth_network_req req;
280
281 /* We have a free port. Check with the secmodel. */
282 if (inp->inp_flags & INP_LOWPORT) {
283 #ifndef IPNOPRIVPORTS
284 req = KAUTH_REQ_NETWORK_BIND_PRIVPORT;
285 #else
286 req = KAUTH_REQ_NETWORK_BIND_PORT;
287 #endif
288 } else
289 req = KAUTH_REQ_NETWORK_BIND_PORT;
290
291 sin.sin_port = port;
292 error = kauth_authorize_network(cred,
293 KAUTH_NETWORK_BIND,
294 req, inp->inp_socket, &sin, NULL);
295 DPRINTF("%s kauth_authorize_network returned %d\n",
296 __func__, error);
297
298 if (error == 0) {
299 DPRINTF("%s port approved\n", __func__);
300 return true; /* KAUTH agrees */
301 }
302 }
303 break;
304 }
305 #endif
306 #ifdef INET6
307 case AF_INET6: { /* IPv6 */
308 struct in6pcb *in6p = (struct in6pcb *)(void *)inp_hdr;
309 struct sockaddr_in6 sin6;
310 void *t;
311
312 if (__BITMAP_ISSET(port, &inet6_reserve))
313 return false;
314
315 sin6.sin6_addr = in6p->in6p_laddr;
316 so = in6p->in6p_socket;
317
318 /* XXX: this is redundant when called from in6_pcbbind */
319 if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 &&
320 ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
321 (so->so_options & SO_ACCEPTCONN) == 0))
322 wild = 1;
323
324 #ifdef INET
325 if (IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) {
326 t = in_pcblookup_port(table,
327 *(struct in_addr *)&sin6.sin6_addr.s6_addr32[3],
328 htons(port), wild, &vestigial);
329 if (!t && vestigial.valid) {
330 DPRINTF("%s in_pcblookup_port returned "
331 "a result\n", __func__);
332 return false;
333 }
334 } else
335 #endif
336 {
337 t = in6_pcblookup_port(table, &sin6.sin6_addr,
338 htons(port), wild, &vestigial);
339 if (!t && vestigial.valid) {
340 DPRINTF("%s in6_pcblookup_port returned "
341 "a result\n", __func__);
342 return false;
343 }
344 }
345 if (t == NULL) {
346 enum kauth_network_req req;
347
348 /* We have a free port. Check with the secmodel. */
349 if (in6p->in6p_flags & IN6P_LOWPORT) {
350 #ifndef IPNOPRIVPORTS
351 req = KAUTH_REQ_NETWORK_BIND_PRIVPORT;
352 #else
353 req = KAUTH_REQ_NETWORK_BIND_PORT;
354 #endif
355 } else {
356 req = KAUTH_REQ_NETWORK_BIND_PORT;
357 }
358
359 sin6.sin6_port = port;
360 error = kauth_authorize_network(cred,
361 KAUTH_NETWORK_BIND, req, so, &sin6, NULL);
362 if (error) {
363 /* Secmodel says no. Keep looking. */
364 DPRINTF("%s secmodel says no\n", __func__);
365 return false;
366 }
367 DPRINTF("%s port approved\n", __func__);
368 return true;
369 }
370 break;
371 }
372 #endif
373 default:
374 DPRINTF("%s unknown address family\n", __func__);
375 return false;
376 }
377 return false;
378 }
379
380 /* This is the default BSD algorithm, as described in RFC 6056 */
381 static int
382 algo_bsd(int algo, uint16_t *port, struct inpcb_hdr *inp_hdr, kauth_cred_t cred)
383 {
384 uint16_t count;
385 uint16_t mymin, mymax, lastport;
386 uint16_t *next_ephemeral;
387 int error;
388
389 DPRINTF("%s called\n", __func__);
390 error = pcb_getports(inp_hdr, &lastport, &mymin, &mymax,
391 &next_ephemeral, algo);
392 if (error)
393 return error;
394 count = mymax - mymin + 1;
395 do {
396 uint16_t myport = *next_ephemeral;
397
398 if (myport < mymin || mymax < myport)
399 myport = mymax;
400 *next_ephemeral = myport - 1;
401 if (check_suitable_port(myport, inp_hdr, cred)) {
402 *port = myport;
403 DPRINTF("%s returning port %d\n", __func__, *port);
404 return 0;
405 }
406 count--;
407 } while (count > 0);
408
409 DPRINTF("%s returning EAGAIN\n", __func__);
410 return EAGAIN;
411 }
412
413 /*
414 * The straightforward algorithm that increments the port number
415 * by a random amount.
416 */
417 static int
418 algo_random_start(int algo, uint16_t *port, struct inpcb_hdr *inp_hdr,
419 kauth_cred_t cred)
420 {
421 uint16_t count, num_ephemeral;
422 uint16_t mymin, mymax, lastport;
423 uint16_t *next_ephemeral;
424 int error;
425
426 DPRINTF("%s called\n", __func__);
427
428 error = pcb_getports(inp_hdr, &lastport, &mymin, &mymax,
429 &next_ephemeral, algo);
430 if (error)
431 return error;
432
433 num_ephemeral = mymax - mymin + 1;
434
435 DPRINTF("num_ephemeral: %u\n", num_ephemeral);
436
437 *next_ephemeral = mymin + (cprng_fast32() % num_ephemeral);
438
439 DPRINTF("next_ephemeral initially: %u\n", *next_ephemeral);
440
441 count = num_ephemeral;
442
443 do {
444 if (check_suitable_port(*next_ephemeral, inp_hdr, cred)) {
445 *port = *next_ephemeral;
446 DPRINTF("%s returning port %d\n", __func__, *port);
447 return 0;
448 }
449 if (*next_ephemeral == mymax) {
450 *next_ephemeral = mymin;
451 } else
452 (*next_ephemeral)++;
453
454 count--;
455
456
457 DPRINTF("next_ephemeral: %u count: %u\n", *next_ephemeral,
458 count);
459
460 } while (count > 0);
461
462 DPRINTF("%s returning EINVAL\n", __func__);
463
464 return EINVAL;
465 }
466
467 /*
468 * Since there is no state kept on the ports tried, we might actually
469 * give up before exhausting the free ports.
470 */
471 static int
472 algo_random_pick(int algo, uint16_t *port, struct inpcb_hdr *inp_hdr,
473 kauth_cred_t cred)
474 {
475 uint16_t count, num_ephemeral;
476 uint16_t mymin, mymax, lastport;
477 uint16_t *next_ephemeral;
478 int error;
479
480 DPRINTF("%s called\n", __func__);
481
482 error = pcb_getports(inp_hdr, &lastport, &mymin, &mymax,
483 &next_ephemeral, algo);
484 if (error)
485 return error;
486
487 num_ephemeral = mymax - mymin + 1;
488
489 DPRINTF("num_ephemeral: %u\n", num_ephemeral);
490 *next_ephemeral = mymin + (cprng_fast32() % num_ephemeral);
491
492 DPRINTF("next_ephemeral initially: %u\n", *next_ephemeral);
493
494 count = num_ephemeral;
495
496 do {
497 if (check_suitable_port(*next_ephemeral, inp_hdr, cred)) {
498 *port = *next_ephemeral;
499 DPRINTF("%s returning port %d\n", __func__, *port);
500 return 0;
501 }
502 *next_ephemeral = mymin +
503 (cprng_fast32() % num_ephemeral);
504
505 count--;
506
507 DPRINTF("next_ephemeral: %u count: %u\n",
508 *next_ephemeral, count);
509 } while (count > 0);
510
511 DPRINTF("%s returning EINVAL\n", __func__);
512
513 return EINVAL;
514 }
515
516 /* This is the implementation from FreeBSD, with tweaks */
517 static uint16_t
518 Fhash(const struct inpcb_hdr *inp_hdr)
519 {
520 MD5_CTX f_ctx;
521 uint32_t Ff[4];
522 uint32_t secret_f[4];
523 uint32_t offset;
524 uint16_t soffset[2];
525
526 cprng_fast(secret_f, sizeof(secret_f));
527
528 MD5Init(&f_ctx);
529 switch (inp_hdr->inph_af) {
530 #ifdef INET
531 case AF_INET: {
532 const struct inpcb *inp =
533 (const struct inpcb *)(const void *)inp_hdr;
534 MD5Update(&f_ctx, (const u_char *)&inp->inp_laddr,
535 sizeof(inp->inp_laddr));
536 MD5Update(&f_ctx, (const u_char *)&inp->inp_faddr,
537 sizeof(inp->inp_faddr));
538 MD5Update(&f_ctx, (const u_char *)&inp->inp_fport,
539 sizeof(inp->inp_fport));
540 break;
541 }
542 #endif
543 #ifdef INET6
544 case AF_INET6: {
545 const struct in6pcb *in6p =
546 (const struct in6pcb *)(const void *)inp_hdr;
547 MD5Update(&f_ctx, (const u_char *)&in6p->in6p_laddr,
548 sizeof(in6p->in6p_laddr));
549 MD5Update(&f_ctx, (const u_char *)&in6p->in6p_faddr,
550 sizeof(in6p->in6p_faddr));
551 MD5Update(&f_ctx, (const u_char *)&in6p->in6p_fport,
552 sizeof(in6p->in6p_fport));
553 break;
554 }
555 #endif
556 default:
557 break;
558 }
559 MD5Update(&f_ctx, (const u_char *)secret_f, sizeof(secret_f));
560 MD5Final((u_char *)&Ff, &f_ctx);
561
562 offset = (Ff[0] ^ Ff[1]) ^ (Ff[2] ^ Ff[3]);
563
564 memcpy(&soffset, &offset, sizeof(soffset));
565
566 return soffset[0] ^ soffset[1];
567 }
568
569 /*
570 * Checks whether the tuple is complete. If not, marks the pcb for
571 * late binding.
572 */
573 static bool
574 iscompletetuple(struct inpcb_hdr *inp_hdr)
575 {
576 #ifdef INET6
577 struct in6pcb *in6p;
578 #endif
579
580 switch (inp_hdr->inph_af) {
581 #ifdef INET
582 case AF_INET: {
583 struct inpcb *inp = (struct inpcb *)(void *)inp_hdr;
584 if (inp->inp_fport == 0 || in_nullhost(inp->inp_faddr)) {
585 DPRINTF("%s fport or faddr missing, delaying port "
586 "to connect/send\n", __func__);
587 inp->inp_bindportonsend = true;
588 return false;
589 } else {
590 inp->inp_bindportonsend = false;
591 }
592 break;
593 }
594 #endif
595 #ifdef INET6
596 case AF_INET6: {
597 in6p = (struct in6pcb *)(void *)inp_hdr;
598 if (in6p->in6p_fport == 0 || memcmp(&in6p->in6p_faddr,
599 &in6addr_any, sizeof(in6p->in6p_faddr)) == 0) {
600 DPRINTF("%s fport or faddr missing, delaying port "
601 "to connect/send\n", __func__);
602 in6p->in6p_bindportonsend = true;
603 return false;
604 } else {
605 in6p->in6p_bindportonsend = false;
606 }
607 break;
608 }
609 #endif
610 default:
611 DPRINTF("%s incorrect address family\n", __func__);
612 return false;
613 }
614
615 return true;
616 }
617
618 static int
619 algo_hash(int algo, uint16_t *port, struct inpcb_hdr *inp_hdr,
620 kauth_cred_t cred)
621 {
622 uint16_t count, num_ephemeral;
623 uint16_t mymin, mymax, lastport;
624 uint16_t *next_ephemeral;
625 uint16_t offset, myport;
626 int error;
627
628 DPRINTF("%s called\n", __func__);
629
630 error = pcb_getports(inp_hdr, &lastport, &mymin, &mymax,
631 &next_ephemeral, algo);
632 if (error)
633 return error;
634
635 if (!iscompletetuple(inp_hdr)) {
636 *port = 0;
637 return 0;
638 }
639
640 /* Ephemeral port selection function */
641 num_ephemeral = mymax - mymin + 1;
642
643 DPRINTF("num_ephemeral: %d\n", num_ephemeral);
644
645 offset = Fhash(inp_hdr);
646
647 count = num_ephemeral;
648 do {
649 myport = mymin + (*next_ephemeral + offset)
650 % num_ephemeral;
651
652 (*next_ephemeral)++;
653
654 if (check_suitable_port(myport, inp_hdr, cred)) {
655 *port = myport;
656 DPRINTF("%s returning port %d\n", __func__, *port);
657 return 0;
658 }
659 count--;
660 } while (count > 0);
661
662 DPRINTF("%s returning EINVAL\n", __func__);
663
664 return EINVAL;
665 }
666
667 static int
668 algo_doublehash(int algo, uint16_t *port, struct inpcb_hdr *inp_hdr,
669 kauth_cred_t cred)
670 {
671 uint16_t count, num_ephemeral;
672 uint16_t mymin, mymax, lastport;
673 uint16_t *next_ephemeral;
674 uint16_t offset, myport;
675 static uint16_t dhtable[8];
676 size_t idx;
677 int error;
678
679 DPRINTF("%s called\n", __func__);
680
681 error = pcb_getports(inp_hdr, &lastport, &mymin, &mymax,
682 &next_ephemeral, algo);
683 if (error)
684 return error;
685
686 if (!iscompletetuple(inp_hdr)) {
687 *port = 0;
688 return 0;
689 }
690 /* first time initialization */
691 if (dhtable[0] == 0)
692 for (size_t i = 0; i < __arraycount(dhtable); i++)
693 dhtable[i] = cprng_fast32() & 0xffff;
694
695 /* Ephemeral port selection function */
696 num_ephemeral = mymax - mymin + 1;
697 offset = Fhash(inp_hdr);
698 idx = Fhash(inp_hdr) % __arraycount(dhtable); /* G */
699 count = num_ephemeral;
700
701 do {
702 myport = mymin + (offset + dhtable[idx])
703 % num_ephemeral;
704 dhtable[idx]++;
705
706 if (check_suitable_port(myport, inp_hdr, cred)) {
707 *port = myport;
708 DPRINTF("%s returning port %d\n", __func__, *port);
709 return 0;
710 }
711 count--;
712
713 } while (count > 0);
714
715 DPRINTF("%s returning EINVAL\n", __func__);
716
717 return EINVAL;
718 }
719
720 static int
721 algo_randinc(int algo, uint16_t *port, struct inpcb_hdr *inp_hdr,
722 kauth_cred_t cred)
723 {
724 static const uint16_t N = 500; /* Determines the trade-off */
725 uint16_t count, num_ephemeral;
726 uint16_t mymin, mymax, lastport;
727 uint16_t *next_ephemeral;
728 uint16_t myport;
729 int error;
730
731 DPRINTF("%s called\n", __func__);
732
733 error = pcb_getports(inp_hdr, &lastport, &mymin, &mymax,
734 &next_ephemeral, algo);
735 if (error)
736 return error;
737
738 if (*next_ephemeral == 0)
739 *next_ephemeral = cprng_fast32() & 0xffff;
740
741 /* Ephemeral port selection function */
742 num_ephemeral = mymax - mymin + 1;
743
744 count = num_ephemeral;
745 do {
746 *next_ephemeral = *next_ephemeral +
747 (cprng_fast32() % N) + 1;
748 myport = mymin +
749 (*next_ephemeral % num_ephemeral);
750
751 if (check_suitable_port(myport, inp_hdr, cred)) {
752 *port = myport;
753 DPRINTF("%s returning port %d\n", __func__, *port);
754 return 0;
755 }
756 count--;
757 } while (count > 0);
758
759 return EINVAL;
760 }
761
762 /* The generic function called in order to pick a port. */
763 int
764 portalgo_randport(uint16_t *port, struct inpcb_hdr *inp_hdr, kauth_cred_t cred)
765 {
766 int algo, error;
767 uint16_t lport;
768 int default_algo;
769
770 DPRINTF("%s called\n", __func__);
771
772 if (inp_hdr->inph_portalgo == PORTALGO_DEFAULT) {
773 switch (inp_hdr->inph_af) {
774 #ifdef INET
775 case AF_INET:
776 default_algo = inet4_portalgo;
777 break;
778 #endif
779 #ifdef INET6
780 case AF_INET6:
781 default_algo = inet6_portalgo;
782 break;
783 #endif
784 default:
785 return EINVAL;
786 }
787
788 if (default_algo == PORTALGO_DEFAULT)
789 algo = PORTALGO_BSD;
790 else
791 algo = default_algo;
792 }
793 else /* socket specifies the algorithm */
794 algo = inp_hdr->inph_portalgo;
795
796 KASSERT(algo >= 0);
797 KASSERT(algo < NALGOS);
798
799 switch (inp_hdr->inph_af) {
800 #ifdef INET
801 case AF_INET: {
802 char buf[INET_ADDRSTRLEN];
803 struct inpcb *inp = (struct inpcb *)(void *)inp_hdr;
804 DPRINTF("local addr: %s\n", IN_PRINT(buf, &inp->inp_laddr));
805 DPRINTF("local port: %d\n", inp->inp_lport);
806 DPRINTF("foreign addr: %s\n", IN_PRINT(buf, &inp->inp_faddr));
807 DPRINTF("foreign port: %d\n", inp->inp_fport);
808 break;
809 }
810 #endif
811 #ifdef INET6
812 case AF_INET6: {
813 char buf[INET6_ADDRSTRLEN];
814 struct in6pcb *in6p = (struct in6pcb *)(void *)inp_hdr;
815
816 DPRINTF("local addr: %s\n", IN6_PRINT(buf, &in6p->in6p_laddr));
817 DPRINTF("local port: %d\n", in6p->in6p_lport);
818 DPRINTF("foreign addr: %s\n", IN6_PRINT(buf,
819 &in6p->in6p_laddr));
820 DPRINTF("foreign port: %d\n", in6p->in6p_fport);
821 break;
822 }
823 #endif
824 default:
825 break;
826 }
827
828 DPRINTF("%s portalgo = %d\n", __func__, algo);
829
830 error = (*algos[algo].func)(algo, &lport, inp_hdr, cred);
831 if (error == 0) {
832 *port = lport;
833 } else if (error != EAGAIN) {
834 uint16_t lastport, mymin, mymax, *pnext_ephemeral;
835
836 error = pcb_getports(inp_hdr, &lastport, &mymin,
837 &mymax, &pnext_ephemeral, algo);
838 if (error)
839 return error;
840 *port = lastport - 1;
841 }
842 return error;
843 }
844
845 /* Sets the algorithm to be used globally */
846 static int
847 portalgo_algo_name_select(const char *name, int *algo)
848 {
849 size_t ai;
850
851 DPRINTF("%s called\n", __func__);
852
853 for (ai = 0; ai < NALGOS; ai++)
854 if (strcmp(algos[ai].name, name) == 0) {
855 DPRINTF("%s: found idx %zu\n", __func__, ai);
856 *algo = ai;
857 return 0;
858 }
859 return EINVAL;
860 }
861
862 /* Sets the algorithm to be used by the pcb inp. */
863 int
864 portalgo_algo_index_select(struct inpcb_hdr *inp, int algo)
865 {
866
867 DPRINTF("%s called with algo %d for pcb %p\n", __func__, algo, inp );
868
869 if ((algo < 0 || algo >= NALGOS) &&
870 (algo != PORTALGO_DEFAULT))
871 return EINVAL;
872
873 inp->inph_portalgo = algo;
874 return 0;
875 }
876
877 /*
878 * The sysctl hook that is supposed to check that we are picking one
879 * of the valid algorithms.
880 */
881 static int
882 sysctl_portalgo_selected(SYSCTLFN_ARGS, int *algo)
883 {
884 struct sysctlnode node;
885 int error;
886 char newalgo[PORTALGO_MAXLEN];
887
888 DPRINTF("%s called\n", __func__);
889
890 strlcpy(newalgo, algos[*algo].name, sizeof(newalgo));
891
892 node = *rnode;
893 node.sysctl_data = newalgo;
894 node.sysctl_size = sizeof(newalgo);
895
896 error = sysctl_lookup(SYSCTLFN_CALL(&node));
897
898 DPRINTF("newalgo: %s\n", newalgo);
899
900 if (error || newp == NULL ||
901 strncmp(newalgo, algos[*algo].name, sizeof(newalgo)) == 0)
902 return error;
903
904 #ifdef KAUTH_NETWORK_SOCKET_PORT_RANDOMIZE
905 if (l != NULL && (error = kauth_authorize_system(l->l_cred,
906 KAUTH_NETWORK_SOCKET, KAUTH_NETWORK_SOCKET_PORT_RANDOMIZE, newname,
907 NULL, NULL)) != 0)
908 return error;
909 #endif
910
911 mutex_enter(softnet_lock);
912 error = portalgo_algo_name_select(newalgo, algo);
913 mutex_exit(softnet_lock);
914 return error;
915 }
916
917 static int
918 sysctl_portalgo_reserve(SYSCTLFN_ARGS, bitmap *bt)
919 {
920 struct sysctlnode node;
921 int error;
922
923 DPRINTF("%s called\n", __func__);
924
925 node = *rnode;
926 node.sysctl_data = bt;
927 node.sysctl_size = sizeof(*bt);
928
929 error = sysctl_lookup(SYSCTLFN_CALL(&node));
930
931 if (error || newp == NULL)
932 return error;
933
934 #ifdef KAUTH_NETWORK_SOCKET_PORT_RESERVE
935 if (l != NULL && (error = kauth_authorize_system(l->l_cred,
936 KAUTH_NETWORK_SOCKET, KAUTH_NETWORK_SOCKET_PORT_RESERVE, bt,
937 NULL, NULL)) != 0)
938 return error;
939 #endif
940 return error;
941 }
942
943 #ifdef INET
944 /*
945 * The sysctl hook that is supposed to check that we are picking one
946 * of the valid algorithms.
947 */
948 int
949 sysctl_portalgo_selected4(SYSCTLFN_ARGS)
950 {
951
952 return sysctl_portalgo_selected(SYSCTLFN_CALL(rnode), &inet4_portalgo);
953 }
954
955 int
956 sysctl_portalgo_reserve4(SYSCTLFN_ARGS)
957 {
958
959 return sysctl_portalgo_reserve(SYSCTLFN_CALL(rnode), &inet4_reserve);
960 }
961 #endif
962
963 #ifdef INET6
964 int
965 sysctl_portalgo_selected6(SYSCTLFN_ARGS)
966 {
967
968 return sysctl_portalgo_selected(SYSCTLFN_CALL(rnode), &inet6_portalgo);
969 }
970
971 int
972 sysctl_portalgo_reserve6(SYSCTLFN_ARGS)
973 {
974 return sysctl_portalgo_reserve(SYSCTLFN_CALL(rnode), &inet6_reserve);
975 }
976 #endif
977
978 /*
979 * The sysctl hook that returns the available
980 * algorithms.
981 */
982 int
983 sysctl_portalgo_available(SYSCTLFN_ARGS)
984 {
985 size_t ai, len = 0;
986 struct sysctlnode node;
987 char availalgo[NALGOS * PORTALGO_MAXLEN];
988
989 DPRINTF("%s called\n", __func__);
990
991 availalgo[0] = '\0';
992
993 for (ai = 0; ai < NALGOS; ai++) {
994 len = strlcat(availalgo, algos[ai].name, sizeof(availalgo));
995 if (ai < NALGOS - 1)
996 strlcat(availalgo, " ", sizeof(availalgo));
997 }
998
999 DPRINTF("available algos: %s\n", availalgo);
1000
1001 node = *rnode;
1002 node.sysctl_data = availalgo;
1003 node.sysctl_size = len;
1004
1005 return sysctl_lookup(SYSCTLFN_CALL(&node));
1006 }
1007