diff options
Diffstat (limited to 'sys/netinet6/ip6_output.c')
-rw-r--r-- | sys/netinet6/ip6_output.c | 119 |
1 files changed, 75 insertions, 44 deletions
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); |