From 413628a7e3d23a897cd959638d325395e4c9691b Mon Sep 17 00:00:00 2001 From: "Bjoern A. Zeeb" Date: Sat, 29 Nov 2008 14:32:14 +0000 Subject: MFp4: Bring in updated jail support from bz_jail branch. This enhances the current jail implementation to permit multiple addresses per jail. In addtion to IPv4, IPv6 is supported as well. Due to updated checks it is even possible to have jails without an IP address at all, which basically gives one a chroot with restricted process view, no networking,.. SCTP support was updated and supports IPv6 in jails as well. Cpuset support permits jails to be bound to specific processor sets after creation. Jails can have an unrestricted (no duplicate protection, etc.) name in addition to the hostname. The jail name cannot be changed from within a jail and is considered to be used for management purposes or as audit-token in the future. DDB 'show jails' command was added to aid debugging. Proper compat support permits 32bit jail binaries to be used on 64bit systems to manage jails. Also backward compatibility was preserved where possible: for jail v1 syscalls, as well as with user space management utilities. Both jail as well as prison version were updated for the new features. A gap was intentionally left as the intermediate versions had been used by various patches floating around the last years. Bump __FreeBSD_version for the afore mentioned and in kernel changes. Special thanks to: - Pawel Jakub Dawidek (pjd) for his multi-IPv4 patches and Olivier Houchard (cognet) for initial single-IPv6 patches. - Jeff Roberson (jeff) and Randall Stewart (rrs) for their help, ideas and review on cpuset and SCTP support. - Robert Watson (rwatson) for lots and lots of help, discussions, suggestions and review of most of the patch at various stages. - John Baldwin (jhb) for his help. - Simon L. Nielsen (simon) as early adopter testing changes on cluster machines as well as all the testers and people who provided feedback the last months on freebsd-jail and other channels. - My employer, CK Software GmbH, for the support so I could work on this. Reviewed by: (see above) MFC after: 3 months (this is just so that I get the mail) X-MFC Before: 7.2-RELEASE if possible --- sys/netinet/in_pcb.c | 162 +++++++++++++++++++++++++++++++--------------- sys/netinet/raw_ip.c | 46 ++++++------- sys/netinet/sctp_pcb.c | 35 +++++----- sys/netinet/sctp_usrreq.c | 77 ++++++++++++++++------ sys/netinet/tcp_usrreq.c | 12 +++- sys/netinet/udp_usrreq.c | 14 ++-- 6 files changed, 221 insertions(+), 125 deletions(-) (limited to 'sys/netinet') diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 9ca1edc8cb13..bb2eae05f1dd 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -294,7 +294,7 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp, struct in_addr laddr; u_short lport = 0; int wild = 0, reuseport = (so->so_options & SO_REUSEPORT); - int error, prison = 0; + int error; int dorandom; /* @@ -323,9 +323,8 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp, if (sin->sin_family != AF_INET) return (EAFNOSUPPORT); #endif - if (sin->sin_addr.s_addr != INADDR_ANY) - if (prison_ip(cred, 0, &sin->sin_addr.s_addr)) - return(EINVAL); + if (prison_local_ip4(cred, &sin->sin_addr)) + return (EINVAL); if (sin->sin_port != *lportp) { /* Don't allow the port to change. */ if (*lportp != 0) @@ -360,14 +359,11 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp, priv_check_cred(cred, PRIV_NETINET_RESERVEDPORT, 0)) return (EACCES); - if (jailed(cred)) - prison = 1; if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr)) && priv_check_cred(inp->inp_cred, PRIV_NETINET_REUSEPORT, 0) != 0) { t = in_pcblookup_local(pcbinfo, sin->sin_addr, - lport, prison ? 0 : INPLOOKUP_WILDCARD, - cred); + lport, INPLOOKUP_WILDCARD, cred); /* * XXX * This entire block sorely needs a rewrite. @@ -384,10 +380,10 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp, t->inp_cred->cr_uid)) return (EADDRINUSE); } - if (prison && prison_ip(cred, 0, &sin->sin_addr.s_addr)) + if (prison_local_ip4(cred, &sin->sin_addr)) return (EADDRNOTAVAIL); t = in_pcblookup_local(pcbinfo, sin->sin_addr, - lport, prison ? 0 : wild, cred); + lport, wild, cred); if (t && (t->inp_vflag & INP_TIMEWAIT)) { /* * XXXRW: If an incpb has had its timewait @@ -419,9 +415,8 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp, u_short first, last, aux; int count; - if (laddr.s_addr != INADDR_ANY) - if (prison_ip(cred, 0, &laddr.s_addr)) - return (EINVAL); + if (prison_local_ip4(cred, &laddr)) + return (EINVAL); if (inp->inp_flags & INP_HIGHPORT) { first = V_ipport_hifirstauto; /* sysctl */ @@ -487,7 +482,7 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp, } while (in_pcblookup_local(pcbinfo, laddr, lport, wild, cred)); } - if (prison_ip(cred, 0, &laddr.s_addr)) + if (prison_local_ip4(cred, &laddr)) return (EINVAL); *laddrp = laddr.s_addr; *lportp = lport; @@ -556,7 +551,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, struct route sro; int error; - KASSERT(laddr != NULL, ("%s: null laddr", __func__)); + KASSERT(laddr != NULL, ("%s: laddr NULL", __func__)); error = 0; ia = NULL; @@ -608,7 +603,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, if (sa->sa_family != AF_INET) continue; sin = (struct sockaddr_in *)sa; - if (htonl(prison_getip(cred)) == sin->sin_addr.s_addr) { + if (prison_check_ip4(cred, &sin->sin_addr)) { ia = (struct in_ifaddr *)ifa; break; } @@ -619,7 +614,8 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, } /* 3. As a last resort return the 'default' jail address. */ - laddr->s_addr = htonl(prison_getip(cred)); + if (prison_getip4(cred, laddr) != 0) + error = EADDRNOTAVAIL; goto done; } @@ -644,7 +640,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, /* Jailed. */ /* 1. Check if the iface address belongs to the jail. */ sin = (struct sockaddr_in *)sro.ro_rt->rt_ifa->ifa_addr; - if (htonl(prison_getip(cred)) == sin->sin_addr.s_addr) { + if (prison_check_ip4(cred, &sin->sin_addr)) { ia = (struct in_ifaddr *)sro.ro_rt->rt_ifa; laddr->s_addr = ia->ia_addr.sin_addr.s_addr; goto done; @@ -660,7 +656,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, if (sa->sa_family != AF_INET) continue; sin = (struct sockaddr_in *)sa; - if (htonl(prison_getip(cred)) == sin->sin_addr.s_addr) { + if (prison_check_ip4(cred, &sin->sin_addr)) { ia = (struct in_ifaddr *)ifa; break; } @@ -671,7 +667,8 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, } /* 3. As a last resort return the 'default' jail address. */ - laddr->s_addr = htonl(prison_getip(cred)); + if (prison_getip4(cred, laddr) != 0) + error = EADDRNOTAVAIL; goto done; } @@ -717,8 +714,7 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, if (sa->sa_family != AF_INET) continue; sin = (struct sockaddr_in *)sa; - if (htonl(prison_getip(cred)) == - sin->sin_addr.s_addr) { + if (prison_check_ip4(cred, &sin->sin_addr)) { ia = (struct in_ifaddr *)ifa; break; } @@ -730,7 +726,8 @@ in_pcbladdr(struct inpcb *inp, struct in_addr *faddr, struct in_addr *laddr, } /* 3. As a last resort return the 'default' jail address. */ - laddr->s_addr = htonl(prison_getip(cred)); + if (prison_getip4(cred, laddr) != 0) + error = EADDRNOTAVAIL; goto done; } @@ -764,7 +761,7 @@ in_pcbconnect_setup(struct inpcb *inp, struct sockaddr *nam, struct sockaddr_in *sin = (struct sockaddr_in *)nam; struct in_ifaddr *ia; struct inpcb *oinp; - struct in_addr laddr, faddr; + struct in_addr laddr, faddr, jailia; u_short lport, fport; int error; @@ -796,9 +793,17 @@ in_pcbconnect_setup(struct inpcb *inp, struct sockaddr *nam, * and the primary interface supports broadcast, * choose the broadcast address for that interface. */ - if (faddr.s_addr == INADDR_ANY) - faddr = IA_SIN(TAILQ_FIRST(&V_in_ifaddrhead))->sin_addr; - else if (faddr.s_addr == (u_long)INADDR_BROADCAST && + if (faddr.s_addr == INADDR_ANY) { + if (cred != NULL && jailed(cred)) { + if (prison_getip4(cred, &jailia) != 0) + return (EADDRNOTAVAIL); + faddr.s_addr = jailia.s_addr; + } else { + faddr = + IA_SIN(TAILQ_FIRST(&V_in_ifaddrhead))-> + sin_addr; + } + } else if (faddr.s_addr == (u_long)INADDR_BROADCAST && (TAILQ_FIRST(&V_in_ifaddrhead)->ia_ifp->if_flags & IFF_BROADCAST)) faddr = satosin(&TAILQ_FIRST( @@ -1115,6 +1120,7 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, struct in_addr laddr, 0, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { #ifdef INET6 + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif @@ -1122,9 +1128,11 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, struct in_addr laddr, inp->inp_laddr.s_addr == laddr.s_addr && inp->inp_lport == lport) { /* - * Found. + * Found? */ - return (inp); + if (cred == NULL || + inp->inp_cred->cr_prison == cred->cr_prison) + return (inp); } } /* @@ -1154,7 +1162,11 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, struct in_addr laddr, */ LIST_FOREACH(inp, &phd->phd_pcblist, inp_portlist) { wildcard = 0; + if (cred != NULL && + inp->inp_cred->cr_prison != cred->cr_prison) + continue; #ifdef INET6 + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV4) == 0) continue; /* @@ -1187,9 +1199,8 @@ in_pcblookup_local(struct inpcbinfo *pcbinfo, struct in_addr laddr, if (wildcard < matchwild) { match = inp; matchwild = wildcard; - if (matchwild == 0) { + if (matchwild == 0) break; - } } } } @@ -1207,7 +1218,7 @@ in_pcblookup_hash(struct inpcbinfo *pcbinfo, struct in_addr faddr, struct ifnet *ifp) { struct inpcbhead *head; - struct inpcb *inp; + struct inpcb *inp, *tmpinp; u_short fport = fport_arg, lport = lport_arg; INP_INFO_LOCK_ASSERT(pcbinfo); @@ -1215,59 +1226,108 @@ in_pcblookup_hash(struct inpcbinfo *pcbinfo, struct in_addr faddr, /* * First look for an exact match. */ + tmpinp = NULL; head = &pcbinfo->ipi_hashbase[INP_PCBHASH(faddr.s_addr, lport, fport, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { #ifdef INET6 + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif if (inp->inp_faddr.s_addr == faddr.s_addr && inp->inp_laddr.s_addr == laddr.s_addr && inp->inp_fport == fport && - inp->inp_lport == lport) - return (inp); + inp->inp_lport == lport) { + /* + * XXX We should be able to directly return + * the inp here, without any checks. + * Well unless both bound with SO_REUSEPORT? + */ + if (jailed(inp->inp_cred)) + return (inp); + if (tmpinp == NULL) + tmpinp = inp; + } } + if (tmpinp != NULL) + return (tmpinp); /* * Then look for a wildcard match, if requested. */ - if (wildcard) { - struct inpcb *local_wild = NULL; + if (wildcard == INPLOOKUP_WILDCARD) { + struct inpcb *local_wild = NULL, *local_exact = NULL; #ifdef INET6 struct inpcb *local_wild_mapped = NULL; #endif + struct inpcb *jail_wild = NULL; + int injail; + + /* + * Order of socket selection - we always prefer jails. + * 1. jailed, non-wild. + * 2. jailed, wild. + * 3. non-jailed, non-wild. + * 4. non-jailed, wild. + */ head = &pcbinfo->ipi_hashbase[INP_PCBHASH(INADDR_ANY, lport, 0, pcbinfo->ipi_hashmask)]; LIST_FOREACH(inp, head, inp_hash) { #ifdef INET6 + /* XXX inp locking */ if ((inp->inp_vflag & INP_IPV4) == 0) continue; #endif - if (inp->inp_faddr.s_addr == INADDR_ANY && - inp->inp_lport == lport) { - if (ifp && ifp->if_type == IFT_FAITH && - (inp->inp_flags & INP_FAITH) == 0) + if (inp->inp_faddr.s_addr != INADDR_ANY || + inp->inp_lport != lport) + continue; + + /* XXX inp locking */ + if (ifp && ifp->if_type == IFT_FAITH && + (inp->inp_flags & INP_FAITH) == 0) + continue; + + injail = jailed(inp->inp_cred); + if (injail) { + if (!prison_check_ip4(inp->inp_cred, &laddr)) continue; - if (inp->inp_laddr.s_addr == laddr.s_addr) + } else { + if (local_exact != NULL) + continue; + } + + if (inp->inp_laddr.s_addr == laddr.s_addr) { + if (injail) return (inp); - else if (inp->inp_laddr.s_addr == INADDR_ANY) { + else + local_exact = inp; + } else if (inp->inp_laddr.s_addr == INADDR_ANY) { #ifdef INET6 - if (inp->inp_vflag & INP_IPV6PROTO) - local_wild_mapped = inp; + /* XXX inp locking, NULL check */ + if (inp->inp_vflag & INP_IPV6PROTO) + local_wild_mapped = inp; + else +#endif /* INET6 */ + if (injail) + jail_wild = inp; else -#endif local_wild = inp; - } } - } + } /* LIST_FOREACH */ + if (jail_wild != NULL) + return (jail_wild); + if (local_exact != NULL) + return (local_exact); + if (local_wild != NULL) + return (local_wild); #ifdef INET6 - if (local_wild == NULL) + if (local_wild_mapped != NULL) return (local_wild_mapped); -#endif - return (local_wild); - } +#endif /* defined(INET6) */ + } /* if (wildcard == INPLOOKUP_WILDCARD) */ + return (NULL); } diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index a84db95ffc45..71eaaa0b9fd1 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -273,10 +273,9 @@ rip_input(struct mbuf *m, int off) continue; if (inp->inp_faddr.s_addr != ip->ip_src.s_addr) continue; - if (jailed(inp->inp_cred) && - (htonl(prison_getip(inp->inp_cred)) != - ip->ip_dst.s_addr)) { - continue; + if (jailed(inp->inp_cred)) { + if (!prison_check_ip4(inp->inp_cred, &ip->ip_dst)) + continue; } if (last) { struct mbuf *n; @@ -304,10 +303,9 @@ rip_input(struct mbuf *m, int off) if (inp->inp_faddr.s_addr && inp->inp_faddr.s_addr != ip->ip_src.s_addr) continue; - if (jailed(inp->inp_cred) && - (htonl(prison_getip(inp->inp_cred)) != - ip->ip_dst.s_addr)) { - continue; + if (jailed(inp->inp_cred)) { + if (!prison_check_ip4(inp->inp_cred, &ip->ip_dst)) + continue; } if (last) { struct mbuf *n; @@ -369,11 +367,15 @@ rip_output(struct mbuf *m, struct socket *so, u_long dst) ip->ip_off = 0; ip->ip_p = inp->inp_ip_p; ip->ip_len = m->m_pkthdr.len; - if (jailed(inp->inp_cred)) - ip->ip_src.s_addr = - htonl(prison_getip(inp->inp_cred)); - else + if (jailed(inp->inp_cred)) { + if (prison_getip4(inp->inp_cred, &ip->ip_src)) { + INP_RUNLOCK(inp); + m_freem(m); + return (EPERM); + } + } else { ip->ip_src = inp->inp_laddr; + } ip->ip_dst.s_addr = dst; ip->ip_ttl = inp->inp_ip_ttl; } else { @@ -383,13 +385,10 @@ rip_output(struct mbuf *m, struct socket *so, u_long dst) } INP_RLOCK(inp); ip = mtod(m, struct ip *); - if (jailed(inp->inp_cred)) { - if (ip->ip_src.s_addr != - htonl(prison_getip(inp->inp_cred))) { - INP_RUNLOCK(inp); - m_freem(m); - return (EPERM); - } + if (!prison_check_ip4(inp->inp_cred, &ip->ip_src)) { + INP_RUNLOCK(inp); + m_freem(m); + return (EPERM); } /* @@ -805,13 +804,8 @@ rip_bind(struct socket *so, struct sockaddr *nam, struct thread *td) if (nam->sa_len != sizeof(*addr)) return (EINVAL); - if (jailed(td->td_ucred)) { - if (addr->sin_addr.s_addr == INADDR_ANY) - addr->sin_addr.s_addr = - htonl(prison_getip(td->td_ucred)); - if (htonl(prison_getip(td->td_ucred)) != addr->sin_addr.s_addr) - return (EADDRNOTAVAIL); - } + if (!prison_check_ip4(td->td_ucred, &addr->sin_addr)) + return (EADDRNOTAVAIL); if (TAILQ_EMPTY(&V_ifnet) || (addr->sin_family != AF_INET && addr->sin_family != AF_IMPLINK) || diff --git a/sys/netinet/sctp_pcb.c b/sys/netinet/sctp_pcb.c index 3fa9eb595b7a..4ce0f1268c86 100644 --- a/sys/netinet/sctp_pcb.c +++ b/sys/netinet/sctp_pcb.c @@ -2553,7 +2553,6 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, struct inpcb *ip_inp; int port_reuse_active = 0; int bindall; - int prison = 0; uint16_t lport; int error; uint32_t vrf_id; @@ -2580,9 +2579,6 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, if (p == NULL) panic("null proc/thread"); #endif - if (p && jailed(p->td_ucred)) { - prison = 1; - } if (addr != NULL) { switch (addr->sa_family) { case AF_INET: @@ -2600,18 +2596,13 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, } sin = (struct sockaddr_in *)addr; lport = sin->sin_port; - if (prison) { - /* - * For INADDR_ANY and LOOPBACK the - * prison_ip() call will transmute - * the ip address to the proper - * value (i.e. the IP address owned - * by the jail). - */ - if (prison_ip(p->td_ucred, 0, &sin->sin_addr.s_addr)) { - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); - return (EINVAL); - } + /* + * For LOOPBACK the prison_local_ip4() call will transmute the ip address + * to the proper value. + */ + if (p && prison_local_ip4(p->td_ucred, &sin->sin_addr) != 0) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); + return (EINVAL); } if (sin->sin_addr.s_addr != INADDR_ANY) { bindall = 0; @@ -2634,12 +2625,16 @@ sctp_inpcb_bind(struct socket *so, struct sockaddr *addr, return (EINVAL); } lport = sin6->sin6_port; + /* - * Jail checks for IPv6 should go HERE! i.e. - * add the prison_ip() equivilant in this - * postion to transmute the addresses to the - * proper one jailed. + * For LOOPBACK the prison_local_ip6() call will transmute the ipv6 address + * to the proper value. */ + if (p && prison_local_ip6(p->td_ucred, &sin6->sin6_addr, + (SCTP_IPV6_V6ONLY(inp) != 0)) != 0) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_PCB, EINVAL); + return (EINVAL); + } if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { bindall = 0; /* KAME hack: embed scopeid */ diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c index 882aee768d5a..058415e4deef 100644 --- a/sys/netinet/sctp_usrreq.c +++ b/sys/netinet/sctp_usrreq.c @@ -507,11 +507,10 @@ sctp_attach(struct socket *so, int proto, struct thread *p) struct inpcb *ip_inp; int error; uint32_t vrf_id = SCTP_DEFAULT_VRFID; - #ifdef IPSEC uint32_t flags; - #endif + inp = (struct sctp_inpcb *)so->so_pcb; if (inp != 0) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); @@ -3951,12 +3950,8 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, struct sctp_getaddresses *addrs; size_t sz; struct thread *td; - int prison = 0; td = (struct thread *)p; - if (jailed(td->td_ucred)) { - prison = 1; - } SCTP_CHECK_AND_CAST(addrs, optval, struct sctp_getaddresses, optsize); if (addrs->addr->sa_family == AF_INET) { @@ -3966,10 +3961,12 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, error = EINVAL; break; } - if (prison && prison_ip(td->td_ucred, 0, &(((struct sockaddr_in *)(addrs->addr))->sin_addr.s_addr))) { + if (td != NULL && prison_local_ip4(td->td_ucred, &(((struct sockaddr_in *)(addrs->addr))->sin_addr))) { SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRNOTAVAIL); error = EADDRNOTAVAIL; + break; } +#ifdef INET6 } else if (addrs->addr->sa_family == AF_INET6) { sz = sizeof(struct sctp_getaddresses) - sizeof(struct sockaddr) + sizeof(struct sockaddr_in6); if (optsize < sz) { @@ -3977,7 +3974,16 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, error = EINVAL; break; } - /* JAIL XXXX Add else here for V6 */ + if (td != NULL && prison_local_ip6(td->td_ucred, &(((struct sockaddr_in6 *)(addrs->addr))->sin6_addr), + (SCTP_IPV6_V6ONLY(inp) != 0)) != 0) { + SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRNOTAVAIL); + error = EADDRNOTAVAIL; + break; + } +#endif + } else { + error = EAFNOSUPPORT; + break; } sctp_bindx_add_address(so, inp, addrs->addr, addrs->sget_assoc_id, vrf_id, @@ -3989,12 +3995,9 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, struct sctp_getaddresses *addrs; size_t sz; struct thread *td; - int prison = 0; td = (struct thread *)p; - if (jailed(td->td_ucred)) { - prison = 1; - } + SCTP_CHECK_AND_CAST(addrs, optval, struct sctp_getaddresses, optsize); if (addrs->addr->sa_family == AF_INET) { sz = sizeof(struct sctp_getaddresses) - sizeof(struct sockaddr) + sizeof(struct sockaddr_in); @@ -4003,10 +4006,12 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, error = EINVAL; break; } - if (prison && prison_ip(td->td_ucred, 0, &(((struct sockaddr_in *)(addrs->addr))->sin_addr.s_addr))) { + if (td != NULL && prison_local_ip4(td->td_ucred, &(((struct sockaddr_in *)(addrs->addr))->sin_addr))) { SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRNOTAVAIL); error = EADDRNOTAVAIL; + break; } +#ifdef INET6 } else if (addrs->addr->sa_family == AF_INET6) { sz = sizeof(struct sctp_getaddresses) - sizeof(struct sockaddr) + sizeof(struct sockaddr_in6); if (optsize < sz) { @@ -4014,7 +4019,16 @@ sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, error = EINVAL; break; } - /* JAIL XXXX Add else here for V6 */ + if (td != NULL && prison_local_ip6(td->td_ucred, &(((struct sockaddr_in6 *)(addrs->addr))->sin6_addr), + (SCTP_IPV6_V6ONLY(inp) != 0)) != 0) { + SCTP_LTRACE_ERR_RET(inp, stcb, NULL, SCTP_FROM_SCTP_USRREQ, EADDRNOTAVAIL); + error = EADDRNOTAVAIL; + break; + } +#endif + } else { + error = EAFNOSUPPORT; + break; } sctp_bindx_delete_address(so, inp, addrs->addr, addrs->sget_assoc_id, vrf_id, @@ -4106,13 +4120,34 @@ sctp_connect(struct socket *so, struct sockaddr *addr, struct thread *p) SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); return EINVAL; } - if ((addr->sa_family == AF_INET6) && (addr->sa_len != sizeof(struct sockaddr_in6))) { - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); - return (EINVAL); - } - if ((addr->sa_family == AF_INET) && (addr->sa_len != sizeof(struct sockaddr_in))) { - SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); - return (EINVAL); +#ifdef INET6 + if (addr->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6p; + if (addr->sa_len != sizeof(struct sockaddr_in6)) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + return (EINVAL); + } + sin6p = (struct sockaddr_in6 *)addr; + if (p != NULL && prison_remote_ip6(p->td_ucred, &sin6p->sin6_addr) != 0) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + return (EINVAL); + } + } else +#endif + if (addr->sa_family == AF_INET) { + struct sockaddr_in *sinp; + if (addr->sa_len != sizeof(struct sockaddr_in)) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + return (EINVAL); + } + sinp = (struct sockaddr_in *)addr; + if (p != NULL && prison_remote_ip4(p->td_ucred, &sinp->sin_addr) != 0) { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); + return (EINVAL); + } + } else { + SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EAFNOSUPPORT); + return (EAFNOSUPPORT); } SCTP_INP_INCR_REF(inp); SCTP_ASOC_CREATE_LOCK(inp); diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index 2ee8aa491e63..6428b642676e 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -440,8 +440,8 @@ tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) if (sinp->sin_family == AF_INET && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr))) return (EAFNOSUPPORT); - if (jailed(td->td_ucred)) - prison_remote_ip(td->td_ucred, 0, &sinp->sin_addr.s_addr); + if (prison_remote_ip4(td->td_ucred, &sinp->sin_addr) != 0) + return (EINVAL); TCPDEBUG0; INP_INFO_WLOCK(&V_tcbinfo); @@ -507,6 +507,10 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) in6_sin6_2_sin(&sin, sin6p); inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; + if (prison_remote_ip4(td->td_ucred, &sin.sin_addr) != 0) { + error = EINVAL; + goto out; + } if ((error = tcp_connect(tp, (struct sockaddr *)&sin, td)) != 0) goto out; error = tcp_output_connect(so, nam); @@ -515,6 +519,10 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td) inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; inp->inp_inc.inc_isipv6 = 1; + if (prison_remote_ip6(td->td_ucred, &sin6p->sin6_addr) != 0) { + error = EINVAL; + goto out; + } if ((error = tcp6_connect(tp, nam, td)) != 0) goto out; error = tcp_output_connect(so, nam); diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index de341afcbe0a..2e523091c74a 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -940,9 +940,10 @@ udp_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr, * Jail may rewrite the destination address, so let it do * that before we use it. */ - if (jailed(td->td_ucred)) - prison_remote_ip(td->td_ucred, 0, - &sin->sin_addr.s_addr); + if (prison_remote_ip4(td->td_ucred, &sin->sin_addr) != 0) { + error = EINVAL; + goto release; + } /* * If a local address or port hasn't yet been selected, or if @@ -1194,8 +1195,11 @@ udp_connect(struct socket *so, struct sockaddr *nam, struct thread *td) return (EISCONN); } sin = (struct sockaddr_in *)nam; - if (jailed(td->td_ucred)) - prison_remote_ip(td->td_ucred, 0, &sin->sin_addr.s_addr); + if (prison_remote_ip4(td->td_ucred, &sin->sin_addr) != 0) { + INP_WUNLOCK(inp); + INP_INFO_WUNLOCK(&udbinfo); + return (EAFNOSUPPORT); + } error = in_pcbconnect(inp, nam, td->td_ucred); if (error == 0) soisconnected(so); -- cgit v1.3