/* * Bind port from sin6 to in6p. */ static int in6_pcbbind_port(struct in6pcb *in6p, struct sockaddr_in6 *sin6, struct lwp *l) { struct inpcbtable *table = in6p->in6p_table; struct socket *so = in6p->in6p_socket; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); int error; if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 && ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || (so->so_options & SO_ACCEPTCONN) == 0)) wild = 1; if (sin6->sin6_port != 0) { enum kauth_network_req req; #ifndef IPNOPRIVPORTS if (ntohs(sin6->sin6_port) < IPV6PORT_RESERVED) req = KAUTH_REQ_NETWORK_BIND_PRIVPORT; else #endif /* IPNOPRIVPORTS */ req = KAUTH_REQ_NETWORK_BIND_PORT; error = kauth_authorize_network(l->l_cred, KAUTH_NETWORK_BIND, req, so, sin6, NULL); if (error) return (EACCES); } if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { /* * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; * allow compepte duplication of binding if * SO_REUSEPORT is set, or if SO_REUSEADDR is set * and a multicast address is bound on both * new and duplicated sockets. */ if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR|SO_REUSEPORT; } if (sin6->sin6_port != 0) { if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { #ifdef INET struct inpcb *t; struct vestigial_inpcb vestige; t = in_pcblookup_port(table, *(struct in_addr *)&sin6->sin6_addr.s6_addr32[3], sin6->sin6_port, wild, &vestige); if (t && (reuseport & t->inp_socket->so_options) == 0) return (EADDRINUSE); if (!t && vestige.valid && !(reuseport && vestige.reuse_port)) return EADDRINUSE; #else return (EADDRNOTAVAIL); #endif } { struct in6pcb *t; struct vestigial_inpcb vestige; t = in6_pcblookup_port(table, &sin6->sin6_addr, sin6->sin6_port, wild, &vestige); if (t && (reuseport & t->in6p_socket->so_options) == 0) return (EADDRINUSE); if (!t && vestige.valid && !(reuseport && vestige.reuse_port)) return EADDRINUSE; } } if (sin6->sin6_port == 0) { int e; e = in6_pcbsetport(sin6, in6p, l); if (e != 0) return (e); } else { in6p->in6p_lport = sin6->sin6_port; in6_pcbstate(in6p, IN6P_BOUND); } LIST_REMOVE(&in6p->in6p_head, inph_lhash); LIST_INSERT_HEAD(IN6PCBHASH_PORT(table, in6p->in6p_lport), &in6p->in6p_head, inph_lhash); return (0); }
static int in_pcbbind_port(struct inpcb *inp, struct sockaddr_in *sin) { struct inpcbtable *table = inp->inp_table; struct socket *so = inp->inp_socket; int reuseport = (so->so_options & SO_REUSEPORT); int wild = 0, error; if (IN_MULTICAST(sin->sin_addr.s_addr)) { /* * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; * allow complete duplication of binding if * SO_REUSEPORT is set, or if SO_REUSEADDR is set * and a multicast address is bound on both * new and duplicated sockets. */ if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR|SO_REUSEPORT; } if (sin->sin_port == 0) { error = in_pcbsetport(sin, inp); if (error) return (error); } else { struct inpcb *t; vestigial_inpcb_t vestige; #ifdef INET6 struct in6pcb *t6; struct in6_addr mapped; #endif //enum kauth_network_req req; if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0) wild = 1; #ifndef IPNOPRIVPORTS // if (ntohs(sin->sin_port) < IPPORT_RESERVED) // req = KAUTH_REQ_NETWORK_BIND_PRIVPORT; // else #endif /* !IPNOPRIVPORTS */ // req = KAUTH_REQ_NETWORK_BIND_PORT; // error = kauth_authorize_network(cred, KAUTH_NETWORK_BIND, req, // so, sin, NULL); // if (error) // return (EACCES); #ifdef INET6 memset(&mapped, 0, sizeof(mapped)); mapped.s6_addr16[5] = 0xffff; memcpy(&mapped.s6_addr32[3], &sin->sin_addr, sizeof(mapped.s6_addr32[3])); t6 = in6_pcblookup_port(table, &mapped, sin->sin_port, wild, &vestige); if (t6 && (reuseport & t6->in6p_socket->so_options) == 0) return (EADDRINUSE); if (!t6 && vestige.valid) { if (!!reuseport != !!vestige.reuse_port) { return EADDRINUSE; } } #endif #if 0 /* VADIM - disabled*/ /* XXX-kauth */ if (so->so_uidinfo->ui_uid && !IN_MULTICAST(sin->sin_addr.s_addr)) { t = in_pcblookup_port(table, sin->sin_addr, sin->sin_port, 1, &vestige); /* * XXX: investigate ramifications of loosening this * restriction so that as long as both ports have * SO_REUSEPORT allow the bind */ if (t && (!in_nullhost(sin->sin_addr) || !in_nullhost(t->inp_laddr) || (t->inp_socket->so_options & SO_REUSEPORT) == 0) && (so->so_uidinfo->ui_uid != t->inp_socket->so_uidinfo->ui_uid)) { return (EADDRINUSE); } if (!t && vestige.valid) { if ((!in_nullhost(sin->sin_addr) || !in_nullhost(vestige.laddr.v4) || !vestige.reuse_port) && so->so_uidinfo->ui_uid != vestige.uid) { return EADDRINUSE; } } } #endif t = in_pcblookup_port(table, sin->sin_addr, sin->sin_port, wild, &vestige); if (t && (reuseport & t->inp_socket->so_options) == 0) return (EADDRINUSE); if (!t && vestige.valid && !(reuseport && vestige.reuse_port)) return EADDRINUSE; inp->inp_lport = sin->sin_port; in_pcbstate(inp, INP_BOUND); } LIST_REMOVE(&inp->inp_head, inph_lhash); LIST_INSERT_HEAD(INPCBHASH_PORT(table, inp->inp_lport), &inp->inp_head, inph_lhash); return (0); }
int in6_pcbbind(void *v, struct mbuf *nam, struct lwp *l) { struct in6pcb *in6p = v; struct socket *so = in6p->in6p_socket; struct inpcbtable *table = in6p->in6p_table; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)NULL; u_int16_t lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); if (in6p->in6p_af != AF_INET6) return (EINVAL); if (in6p->in6p_lport || !IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) return (EINVAL); if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 && ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || (so->so_options & SO_ACCEPTCONN) == 0)) wild = 1; if (nam) { int error; sin6 = mtod(nam, struct sockaddr_in6 *); if (nam->m_len != sizeof(*sin6)) return (EINVAL); /* * We should check the family, but old programs * incorrectly fail to intialize it. */ if (sin6->sin6_family != AF_INET6) return (EAFNOSUPPORT); #ifndef INET if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) return (EADDRNOTAVAIL); #endif if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0) return (error); lport = sin6->sin6_port; if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { /* * Treat SO_REUSEADDR as SO_REUSEPORT for multicast; * allow compepte duplication of binding if * SO_REUSEPORT is set, or if SO_REUSEADDR is set * and a multicast address is bound on both * new and duplicated sockets. */ if (so->so_options & SO_REUSEADDR) reuseport = SO_REUSEADDR|SO_REUSEPORT; } else if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY) != 0) return (EINVAL); if (sin6->sin6_addr.s6_addr32[3]) { struct sockaddr_in sin; bzero(&sin, sizeof(sin)); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; bcopy(&sin6->sin6_addr.s6_addr32[3], &sin.sin_addr, sizeof(sin.sin_addr)); if (ifa_ifwithaddr((struct sockaddr *)&sin) == 0) return EADDRNOTAVAIL; } } else if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { struct ifaddr *ia = NULL; sin6->sin6_port = 0; /* yech... */ if ((in6p->in6p_flags & IN6P_FAITH) == 0 && (ia = ifa_ifwithaddr((struct sockaddr *)sin6)) == 0) return (EADDRNOTAVAIL); /* * bind to an anycast address might accidentally * cause sending a packet with an anycast source * address, so we forbid it. * * We should allow to bind to a deprecated address, * since the application dare to use it. * But, can we assume that they are careful enough * to check if the address is deprecated or not? * Maybe, as a safeguard, we should have a setsockopt * flag to control the bind(2) behavior against * deprecated addresses (default: forbid bind(2)). */ if (ia && ((struct in6_ifaddr *)ia)->ia6_flags & (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY|IN6_IFF_DETACHED)) return (EADDRNOTAVAIL); } if (lport) { #ifndef IPNOPRIVPORTS int priv; /* * NOTE: all operating systems use suser() for * privilege check! do not rewrite it into SS_PRIV. */ priv = (l && !kauth_authorize_generic(l->l_cred, KAUTH_GENERIC_ISSUSER, NULL)) ? 1 : 0; /* GROSS */ if (ntohs(lport) < IPV6PORT_RESERVED && !priv) return (EACCES); #endif if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { #ifdef INET struct inpcb *t; t = in_pcblookup_port(table, *(struct in_addr *)&sin6->sin6_addr.s6_addr32[3], lport, wild); if (t && (reuseport & t->inp_socket->so_options) == 0) return (EADDRINUSE); #else return (EADDRNOTAVAIL); #endif } { struct in6pcb *t; t = in6_pcblookup_port(table, &sin6->sin6_addr, lport, wild); if (t && (reuseport & t->in6p_socket->so_options) == 0) return (EADDRINUSE); } } in6p->in6p_laddr = sin6->sin6_addr; } if (lport == 0) { int e; e = in6_pcbsetport(&in6p->in6p_laddr, in6p, l); if (e != 0) return (e); } else { in6p->in6p_lport = lport; in6_pcbstate(in6p, IN6P_BOUND); } LIST_REMOVE(&in6p->in6p_head, inph_lhash); LIST_INSERT_HEAD(IN6PCBHASH_PORT(table, in6p->in6p_lport), &in6p->in6p_head, inph_lhash); #if 0 in6p->in6p_flowinfo = 0; /* XXX */ #endif return (0); }
/* * Check whether the port picked by the port randomizer is available * and whether KAUTH approves of our choice. This part of the code * shamelessly copied from in_pcb.c. */ static bool check_suitable_port(uint16_t port, struct inpcb_hdr *inp_hdr, kauth_cred_t cred) { struct inpcbtable * const table = inp_hdr->inph_table; #ifdef INET vestigial_inpcb_t vestigial; #endif int error; #ifdef INET6 struct socket *so; int wild = 0; #endif DPRINTF("%s called for argument %d\n", __func__, port); switch (inp_hdr->inph_af) { #ifdef INET case AF_INET: { /* IPv4 */ struct inpcb *inp = (struct inpcb *)(void *)inp_hdr; struct inpcb *pcb; struct sockaddr_in sin; if (__BITMAP_ISSET(port, &inet4_reserve)) return false; sin.sin_addr = inp->inp_laddr; pcb = in_pcblookup_port(table, sin.sin_addr, htons(port), 1, &vestigial); DPRINTF("%s in_pcblookup_port returned %p and " "vestigial.valid %d\n", __func__, pcb, vestigial.valid); if ((!pcb) && (!vestigial.valid)) { enum kauth_network_req req; /* We have a free port. Check with the secmodel. */ if (inp->inp_flags & INP_LOWPORT) { #ifndef IPNOPRIVPORTS req = KAUTH_REQ_NETWORK_BIND_PRIVPORT; #else req = KAUTH_REQ_NETWORK_BIND_PORT; #endif } else req = KAUTH_REQ_NETWORK_BIND_PORT; sin.sin_port = port; error = kauth_authorize_network(cred, KAUTH_NETWORK_BIND, req, inp->inp_socket, &sin, NULL); DPRINTF("%s kauth_authorize_network returned %d\n", __func__, error); if (error == 0) { DPRINTF("%s port approved\n", __func__); return true; /* KAUTH agrees */ } } break; } #endif #ifdef INET6 case AF_INET6: { /* IPv6 */ struct in6pcb *in6p = (struct in6pcb *)(void *)inp_hdr; struct sockaddr_in6 sin6; void *t; if (__BITMAP_ISSET(port, &inet6_reserve)) return false; sin6.sin6_addr = in6p->in6p_laddr; so = in6p->in6p_socket; /* XXX: this is redundant when called from in6_pcbbind */ if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0 && ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 || (so->so_options & SO_ACCEPTCONN) == 0)) wild = 1; #ifdef INET if (IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) { t = in_pcblookup_port(table, *(struct in_addr *)&sin6.sin6_addr.s6_addr32[3], htons(port), wild, &vestigial); if (!t && vestigial.valid) { DPRINTF("%s in_pcblookup_port returned " "a result\n", __func__); return false; } } else #endif { t = in6_pcblookup_port(table, &sin6.sin6_addr, htons(port), wild, &vestigial); if (!t && vestigial.valid) { DPRINTF("%s in6_pcblookup_port returned " "a result\n", __func__); return false; } } if (t == NULL) { enum kauth_network_req req; /* We have a free port. Check with the secmodel. */ if (in6p->in6p_flags & IN6P_LOWPORT) { #ifndef IPNOPRIVPORTS req = KAUTH_REQ_NETWORK_BIND_PRIVPORT; #else req = KAUTH_REQ_NETWORK_BIND_PORT; #endif } else { req = KAUTH_REQ_NETWORK_BIND_PORT; } sin6.sin6_port = port; error = kauth_authorize_network(cred, KAUTH_NETWORK_BIND, req, so, &sin6, NULL); if (error) { /* Secmodel says no. Keep looking. */ DPRINTF("%s secmodel says no\n", __func__); return false; } DPRINTF("%s port approved\n", __func__); return true; } break; } #endif default: DPRINTF("%s unknown address family\n", __func__); return false; } return false; }