/*
 * 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);
}
Beispiel #2
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);
}
Beispiel #3
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);
}
Beispiel #4
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;
}