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