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:
Bjoern A. Zeeb 2008-01-24 08:25:59 +00:00
parent 107d12440a
commit 79ba395267
8 changed files with 100 additions and 69 deletions

View File

@ -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.
*/

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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 *));

View File

@ -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)

View File

@ -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

View File

@ -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.