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