diff options
-rw-r--r-- | sys/kern/kern_jail.c | 6 | ||||
-rw-r--r-- | sys/netinet/ip_options.c | 1 | ||||
-rw-r--r-- | sys/netinet6/in6.c | 20 | ||||
-rw-r--r-- | sys/netinet6/ip6_output.c | 119 | ||||
-rw-r--r-- | sys/netinet6/ip6_var.h | 2 | ||||
-rw-r--r-- | sys/netinet6/raw_ip6.c | 11 | ||||
-rw-r--r-- | sys/netinet6/udp6_usrreq.c | 7 | ||||
-rw-r--r-- | sys/sys/priv.h | 1 |
8 files changed, 99 insertions, 68 deletions
diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index 9e7442f..62fae6f 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -720,6 +720,12 @@ prison_priv_check(struct ucred *cred, int priv) return (0); /* + * Allow jailed root to set certian IPv4/6 (option) headers. + */ + case PRIV_NETINET_SETHDROPTS: + return (0); + + /* * Conditionally allow creating raw sockets in jail. */ case PRIV_NETINET_RAW: diff --git a/sys/netinet/ip_options.c b/sys/netinet/ip_options.c index f190df1..6eeec01 100644 --- a/sys/netinet/ip_options.c +++ b/sys/netinet/ip_options.c @@ -651,6 +651,7 @@ ip_pcbopts(struct inpcb *inp, int optname, struct mbuf *m) * in actual IP option, but is stored before the * options. */ + /* XXX-BZ PRIV_NETINET_SETHDROPTS? */ if (optlen < IPOPT_MINOFF - 1 + sizeof(struct in_addr)) goto bad; m->m_len -= sizeof(struct in_addr); diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c index 450b130..bcb634f 100644 --- a/sys/netinet6/in6.c +++ b/sys/netinet6/in6.c @@ -401,13 +401,16 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, switch (cmd) { case SIOCALIFADDR: + if (td != NULL) { + error = priv_check(td, PRIV_NET_ADDIFADDR); + if (error) + return (error); + } + return in6_lifaddr_ioctl(so, cmd, data, ifp, td); + case SIOCDLIFADDR: - /* - * XXXRW: Is this checked at another layer? What priv to use - * here? - */ if (td != NULL) { - error = suser(td); + error = priv_check(td, PRIV_NET_DELIFADDR); if (error) return (error); } @@ -500,12 +503,9 @@ in6_control(struct socket *so, u_long cmd, caddr_t data, ifra->ifra_addr.sin6_len != sizeof(struct sockaddr_in6)) return (EAFNOSUPPORT); - /* - * XXXRW: Is this checked at another layer? What priv to use - * here? - */ if (td != NULL) { - error = suser(td); + error = priv_check(td, (cmd == SIOCDIFADDR_IN6) ? + PRIV_NET_DELIFADDR : PRIV_NET_ADDIFADDR); if (error) return (error); } diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index a79363e..4b6c52e 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -114,12 +114,12 @@ struct ip6_exthdrs { }; static int ip6_pcbopt __P((int, u_char *, int, struct ip6_pktopts **, - int, int)); + struct ucred *, int)); static int ip6_pcbopts __P((struct ip6_pktopts **, struct mbuf *, struct socket *, struct sockopt *)); static int ip6_getpcbopt(struct ip6_pktopts *, int, struct sockopt *); -static int ip6_setpktopt __P((int, u_char *, int, struct ip6_pktopts *, int, - int, int, int)); +static int ip6_setpktopt __P((int, u_char *, int, struct ip6_pktopts *, + struct ucred *, int, int, int)); static int ip6_setmoptions(int, struct ip6_moptions **, struct mbuf *); static int ip6_getmoptions(int, struct ip6_moptions *, struct mbuf **); @@ -1346,7 +1346,7 @@ ip6_getpmtu(struct route_in6 *ro_pmtu, struct route_in6 *ro, int ip6_ctloutput(struct socket *so, struct sockopt *sopt) { - int privileged, optdatalen, uproto; + int optdatalen, uproto; void *optdata; struct inpcb *in6p = sotoinpcb(so); int error, optval; @@ -1365,7 +1365,6 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt) } error = optval = 0; - privileged = (td == 0 || suser(td)) ? 0 : 1; uproto = (int)so->so_proto->pr_protocol; if (level == IPPROTO_IPV6) { @@ -1408,9 +1407,11 @@ ip6_ctloutput(struct socket *so, struct sockopt *sopt) case IPV6_RECVHOPOPTS: case IPV6_RECVDSTOPTS: case IPV6_RECVRTHDRDSTOPTS: - if (!privileged) { - error = EPERM; - break; + if (td != NULL) { + error = priv_check(td, + PRIV_NETINET_SETHDROPTS); + if (error) + break; } /* FALLTHROUGH */ case IPV6_UNICAST_HOPS: @@ -1482,10 +1483,9 @@ do { \ } optp = &in6p->in6p_outputopts; error = ip6_pcbopt(IPV6_HOPLIMIT, - (u_char *)&optval, - sizeof(optval), - optp, - privileged, uproto); + (u_char *)&optval, sizeof(optval), + optp, (td != NULL) ? td->td_ucred : + NULL, uproto); break; } @@ -1597,10 +1597,9 @@ do { \ struct ip6_pktopts **optp; optp = &in6p->in6p_outputopts; error = ip6_pcbopt(optname, - (u_char *)&optval, - sizeof(optval), - optp, - privileged, uproto); + (u_char *)&optval, sizeof(optval), + optp, (td != NULL) ? td->td_ucred : + NULL, uproto); break; } @@ -1630,13 +1629,21 @@ do { \ * Check super-user privilege. * See comments for IPV6_RECVHOPOPTS. */ - if (!privileged) - return (EPERM); + if (td != NULL) { + error = priv_check(td, + PRIV_NETINET_SETHDROPTS); + if (error) + return (error); + } OPTSET2292(IN6P_HOPOPTS); break; case IPV6_2292DSTOPTS: - if (!privileged) - return (EPERM); + if (td != NULL) { + error = priv_check(td, + PRIV_NETINET_SETHDROPTS); + if (error) + return (error); + } OPTSET2292(IN6P_DSTOPTS|IN6P_RTHDRDSTOPTS); /* XXX */ break; case IPV6_2292RTHDR: @@ -1675,9 +1682,9 @@ do { \ optlen = sopt->sopt_valsize; optbuf = optbuf_storage; optp = &in6p->in6p_outputopts; - error = ip6_pcbopt(optname, - optbuf, optlen, - optp, privileged, uproto); + error = ip6_pcbopt(optname, optbuf, optlen, + optp, (td != NULL) ? td->td_ucred : NULL, + uproto); break; } #undef OPTSET @@ -1764,6 +1771,7 @@ do { \ caddr_t req = NULL; size_t len = 0; struct mbuf *m; + int priv = 0; if ((error = soopt_getm(sopt, &m)) != 0) /* XXX */ break; @@ -1773,8 +1781,22 @@ do { \ req = mtod(m, caddr_t); len = m->m_len; } + if (sopt->sopt_td != NULL) { + /* + * XXXRW/XXX-BZ: Would be more desirable to do + * this one layer down so that we only exercise + * privilege if it is needed. + */ + error = priv_check(sopt->sopt_td, + PRIV_NETINET_IPSEC); + if (error) + priv = 0; + else + priv = 1; + } else + priv = 1; error = ipsec6_set_policy(in6p, optname, req, - len, privileged); + len, priv); m_freem(m); } break; @@ -2103,7 +2125,6 @@ ip6_pcbopts(struct ip6_pktopts **pktopt, struct mbuf *m, struct ip6_pktopts *opt = *pktopt; int error = 0; struct thread *td = sopt->sopt_td; - int priv = 0; /* turn off any old options. */ if (opt) { @@ -2128,10 +2149,8 @@ ip6_pcbopts(struct ip6_pktopts **pktopt, struct mbuf *m, } /* set options specified by user. */ - if (td && !suser(td)) - priv = 1; - if ((error = ip6_setpktopts(m, opt, NULL, priv, - so->so_proto->pr_protocol)) != 0) { + if ((error = ip6_setpktopts(m, opt, NULL, (td != NULL) ? + td->td_ucred : NULL, so->so_proto->pr_protocol)) != 0) { ip6_clearpktopts(opt, -1); /* XXX: discard all options */ free(opt, M_IP6OPT); return (error); @@ -2157,7 +2176,7 @@ ip6_initpktopts(struct ip6_pktopts *opt) static int ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt, - int priv, int uproto) + struct ucred *cred, int uproto) { struct ip6_pktopts *opt; @@ -2168,7 +2187,7 @@ ip6_pcbopt(int optname, u_char *buf, int len, struct ip6_pktopts **pktopt, } opt = *pktopt; - return (ip6_setpktopt(optname, buf, len, opt, priv, 1, 0, uproto)); + return (ip6_setpktopt(optname, buf, len, opt, cred, 1, 0, uproto)); } static int @@ -2411,7 +2430,6 @@ ip6_setmoptions(int optname, struct ip6_moptions **im6op, struct mbuf *m) struct ip6_moptions *im6o = *im6op; struct route_in6 ro; struct in6_multi_mship *imm; - struct thread *td = curthread; if (im6o == NULL) { /* @@ -2507,10 +2525,10 @@ ip6_setmoptions(int optname, struct ip6_moptions **im6op, struct mbuf *m) * all multicast addresses. Only super user is allowed * to do this. */ - if (suser(td)) { - error = EACCES; + /* XXX-BZ might need a better PRIV_NETINET_x for this */ + error = priv_check(curthread, PRIV_NETINET_MROUTE); + if (error) break; - } } else if (!IN6_IS_ADDR_MULTICAST(&mreq->ipv6mr_multiaddr)) { error = EINVAL; break; @@ -2769,7 +2787,7 @@ ip6_freemoptions(struct ip6_moptions *im6o) */ int ip6_setpktopts(struct mbuf *control, struct ip6_pktopts *opt, - struct ip6_pktopts *stickyopt, int priv, int uproto) + struct ip6_pktopts *stickyopt, struct ucred *cred, int uproto) { struct cmsghdr *cm = 0; @@ -2814,7 +2832,7 @@ ip6_setpktopts(struct mbuf *control, struct ip6_pktopts *opt, continue; error = ip6_setpktopt(cm->cmsg_type, CMSG_DATA(cm), - cm->cmsg_len - CMSG_LEN(0), opt, priv, 0, 1, uproto); + cm->cmsg_len - CMSG_LEN(0), opt, cred, 0, 1, uproto); if (error) return (error); } @@ -2833,9 +2851,10 @@ ip6_setpktopts(struct mbuf *control, struct ip6_pktopts *opt, */ static int ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt, - int priv, int sticky, int cmsg, int uproto) + struct ucred *cred, int sticky, int cmsg, int uproto) { int minmtupolicy, preftemp; + int error; if (!sticky && !cmsg) { #ifdef DIAGNOSTIC @@ -2977,8 +2996,12 @@ ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt, case IPV6_2292NEXTHOP: case IPV6_NEXTHOP: - if (!priv) - return (EPERM); + if (cred != NULL) { + error = priv_check_cred(cred, + PRIV_NETINET_SETHDROPTS, 0); + if (error) + return (error); + } if (len == 0) { /* just remove the option */ ip6_clearpktopts(opt, IPV6_NEXTHOP); @@ -3032,8 +3055,12 @@ ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt, * options, since per-option restriction has too much * overhead. */ - if (!priv) - return (EPERM); + if (cred != NULL) { + error = priv_check_cred(cred, + PRIV_NETINET_SETHDROPTS, 0); + if (error) + return (error); + } if (len == 0) { ip6_clearpktopts(opt, IPV6_HOPOPTS); @@ -3065,8 +3092,12 @@ ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt, struct ip6_dest *dest, **newdest = NULL; int destlen; - if (!priv) /* XXX: see the comment for IPV6_HOPOPTS */ - return (EPERM); + if (cred != NULL) { /* XXX: see the comment for IPV6_HOPOPTS */ + error = priv_check_cred(cred, + PRIV_NETINET_SETHDROPTS, 0); + if (error) + return (error); + } if (len == 0) { ip6_clearpktopts(opt, optname); diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index 1d34bfe..65707d6 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -367,7 +367,7 @@ int ip6_ctloutput __P((struct socket *, struct sockopt *)); int ip6_raw_ctloutput __P((struct socket *, struct sockopt *)); void ip6_initpktopts __P((struct ip6_pktopts *)); int ip6_setpktopts __P((struct mbuf *, struct ip6_pktopts *, - struct ip6_pktopts *, int, int)); + struct ip6_pktopts *, struct ucred *, int)); void ip6_clearpktopts __P((struct ip6_pktopts *, int)); struct ip6_pktopts *ip6_copypktopts __P((struct ip6_pktopts *, int)); int ip6_optlen __P((struct inpcb *)); diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c index e8b5aa2..30fcbea 100644 --- a/sys/netinet6/raw_ip6.c +++ b/sys/netinet6/raw_ip6.c @@ -324,7 +324,6 @@ rip6_output(m, va_alist) struct ip6_pktopts opt, *optp; struct ifnet *oifp = NULL; int type = 0, code = 0; /* for ICMPv6 output statistics only */ - int priv = 0; int scope_ambiguous = 0; struct in6_addr *in6a; va_list ap; @@ -338,14 +337,11 @@ rip6_output(m, va_alist) in6p = sotoin6pcb(so); INP_LOCK(in6p); - priv = 0; - if (suser_cred(so->so_cred, 0) == 0) - priv = 1; dst = &dstsock->sin6_addr; if (control) { if ((error = ip6_setpktopts(control, &opt, - in6p->in6p_outputopts, priv, so->so_proto->pr_protocol)) - != 0) { + in6p->in6p_outputopts, so->so_cred, + so->so_proto->pr_protocol)) != 0) { goto bad; } optp = &opt; @@ -548,7 +544,8 @@ rip6_attach(struct socket *so, int proto, struct thread *td) inp = sotoinpcb(so); KASSERT(inp == NULL, ("rip6_attach: inp != NULL")); - if (td && (error = suser(td)) != 0) + error = priv_check(td, PRIV_NETINET_RAW); + if (error) return error; error = soreserve(so, rip_sendspace, rip_recvspace); if (error) diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index f9f3297..803e32d 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -488,17 +488,12 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6, u_short fport; int error = 0; struct ip6_pktopts *optp, opt; - int priv; int af = AF_INET6, hlen = sizeof(struct ip6_hdr); int flags; struct sockaddr_in6 tmp; INP_LOCK_ASSERT(inp); - priv = 0; - if (td && !suser(td)) - priv = 1; - if (addr6) { /* addr6 has been validated in udp6_send(). */ sin6 = (struct sockaddr_in6 *)addr6; @@ -523,7 +518,7 @@ udp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6, if (control) { if ((error = ip6_setpktopts(control, &opt, - inp->in6p_outputopts, priv, IPPROTO_UDP)) != 0) + inp->in6p_outputopts, td->td_ucred, IPPROTO_UDP)) != 0) goto release; optp = &opt; } else diff --git a/sys/sys/priv.h b/sys/sys/priv.h index deac09c..8b89bce 100644 --- a/sys/sys/priv.h +++ b/sys/sys/priv.h @@ -375,6 +375,7 @@ #define PRIV_NETINET_ALIFETIME6 502 /* Administer IPv6 address lifetimes. */ #define PRIV_NETINET_IPSEC 503 /* Administer IPSEC. */ #define PRIV_NETINET_REUSEPORT 504 /* Allow [rapid] port/address reuse. */ +#define PRIV_NETINET_SETHDROPTS 505 /* Set certain IPv4/6 header options. */ /* * IPX/SPX privileges. |