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