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