Replace the last susers calls in netinet6/ with privilege checks.
Introduce a new privilege allowing to set certain IP header options (hop-by-hop, routing headers). Leave a few comments to be addressed later. Reviewed by: rwatson (older version, before addressing his comments)
This commit is contained in:
parent
107d12440a
commit
79ba395267
@ -719,6 +719,12 @@ prison_priv_check(struct ucred *cred, int priv)
|
||||
case PRIV_NETINET_REUSEPORT:
|
||||
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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -401,13 +401,16 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
|
||||
|
||||
switch (cmd) {
|
||||
case SIOCALIFADDR:
|
||||
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_ADDIFADDR);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
return in6_lifaddr_ioctl(so, cmd, data, ifp, td);
|
||||
|
||||
case SIOCDLIFADDR:
|
||||
if (td != NULL) {
|
||||
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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 *));
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user