Skip to content

Commit

Permalink
net: Use netlink_ns_capable to verify the permisions of netlink messages
Browse files Browse the repository at this point in the history
It is possible by passing a netlink socket to a more privileged
executable and then to fool that executable into writing to the socket
data that happens to be valid netlink message to do something that
privileged executable did not intend to do.

To keep this from happening replace bare capable and ns_capable calls
with netlink_capable, netlink_net_calls and netlink_ns_capable calls.
Which act the same as the previous calls except they verify that the
opener of the socket had the desired permissions as well.

Reported-by: Andy Lutomirski <[email protected]>
Signed-off-by: "Eric W. Biederman" <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
ebiederm authored and davem330 committed Apr 24, 2014
1 parent aa4cf94 commit 90f62cf
Show file tree
Hide file tree
Showing 19 changed files with 38 additions and 36 deletions.
2 changes: 1 addition & 1 deletion crypto/crypto_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
type -= CRYPTO_MSG_BASE;
link = &crypto_dispatch[type];

if (!capable(CAP_NET_ADMIN))
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

if ((type == (CRYPTO_MSG_GETALG - CRYPTO_MSG_BASE) &&
Expand Down
2 changes: 1 addition & 1 deletion drivers/connector/cn_proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ static void cn_proc_mcast_ctl(struct cn_msg *msg,
return;

/* Can only change if privileged. */
if (!capable(CAP_NET_ADMIN)) {
if (!__netlink_ns_capable(nsp, &init_user_ns, CAP_NET_ADMIN)) {
err = EPERM;
goto out;
}
Expand Down
2 changes: 1 addition & 1 deletion drivers/scsi/scsi_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb)
goto next_msg;
}

if (!capable(CAP_SYS_ADMIN)) {
if (!netlink_capable(skb, CAP_SYS_ADMIN)) {
err = -EPERM;
goto next_msg;
}
Expand Down
4 changes: 2 additions & 2 deletions kernel/audit.c
Original file line number Diff line number Diff line change
Expand Up @@ -643,13 +643,13 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type)
if ((task_active_pid_ns(current) != &init_pid_ns))
return -EPERM;

if (!capable(CAP_AUDIT_CONTROL))
if (!netlink_capable(skb, CAP_AUDIT_CONTROL))
err = -EPERM;
break;
case AUDIT_USER:
case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG:
case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2:
if (!capable(CAP_AUDIT_WRITE))
if (!netlink_capable(skb, CAP_AUDIT_WRITE))
err = -EPERM;
break;
default: /* bad msg */
Expand Down
4 changes: 2 additions & 2 deletions net/can/gw.c
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,7 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh)
u8 limhops = 0;
int err = 0;

if (!capable(CAP_NET_ADMIN))
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

if (nlmsg_len(nlh) < sizeof(*r))
Expand Down Expand Up @@ -893,7 +893,7 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh)
u8 limhops = 0;
int err = 0;

if (!capable(CAP_NET_ADMIN))
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

if (nlmsg_len(nlh) < sizeof(*r))
Expand Down
20 changes: 11 additions & 9 deletions net/core/rtnetlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -1395,7 +1395,8 @@ static int do_set_master(struct net_device *dev, int ifindex)
return 0;
}

static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
static int do_setlink(const struct sk_buff *skb,
struct net_device *dev, struct ifinfomsg *ifm,
struct nlattr **tb, char *ifname, int modified)
{
const struct net_device_ops *ops = dev->netdev_ops;
Expand All @@ -1407,7 +1408,7 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
err = PTR_ERR(net);
goto errout;
}
if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) {
if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
err = -EPERM;
goto errout;
}
Expand Down Expand Up @@ -1661,7 +1662,7 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
if (err < 0)
goto errout;

err = do_setlink(dev, ifm, tb, ifname, 0);
err = do_setlink(skb, dev, ifm, tb, ifname, 0);
errout:
return err;
}
Expand Down Expand Up @@ -1778,7 +1779,8 @@ struct net_device *rtnl_create_link(struct net *net,
}
EXPORT_SYMBOL(rtnl_create_link);

static int rtnl_group_changelink(struct net *net, int group,
static int rtnl_group_changelink(const struct sk_buff *skb,
struct net *net, int group,
struct ifinfomsg *ifm,
struct nlattr **tb)
{
Expand All @@ -1787,7 +1789,7 @@ static int rtnl_group_changelink(struct net *net, int group,

for_each_netdev(net, dev) {
if (dev->group == group) {
err = do_setlink(dev, ifm, tb, NULL, 0);
err = do_setlink(skb, dev, ifm, tb, NULL, 0);
if (err < 0)
return err;
}
Expand Down Expand Up @@ -1929,12 +1931,12 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh)
modified = 1;
}

return do_setlink(dev, ifm, tb, ifname, modified);
return do_setlink(skb, dev, ifm, tb, ifname, modified);
}

if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
if (ifm->ifi_index == 0 && tb[IFLA_GROUP])
return rtnl_group_changelink(net,
return rtnl_group_changelink(skb, net,
nla_get_u32(tb[IFLA_GROUP]),
ifm, tb);
return -ENODEV;
Expand Down Expand Up @@ -2321,7 +2323,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
int err = -EINVAL;
__u8 *addr;

if (!capable(CAP_NET_ADMIN))
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
Expand Down Expand Up @@ -2773,7 +2775,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
sz_idx = type>>2;
kind = type&3;

if (kind != 2 && !ns_capable(net->user_ns, CAP_NET_ADMIN))
if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN))
return -EPERM;

if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
Expand Down
2 changes: 1 addition & 1 deletion net/dcb/dcbnl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1669,7 +1669,7 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh)
struct nlmsghdr *reply_nlh = NULL;
const struct reply_func *fn;

if ((nlh->nlmsg_type == RTM_SETDCB) && !capable(CAP_NET_ADMIN))
if ((nlh->nlmsg_type == RTM_SETDCB) && !netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX,
Expand Down
4 changes: 2 additions & 2 deletions net/decnet/dn_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
struct dn_ifaddr __rcu **ifap;
int err = -EINVAL;

if (!capable(CAP_NET_ADMIN))
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

if (!net_eq(net, &init_net))
Expand Down Expand Up @@ -618,7 +618,7 @@ static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
struct dn_ifaddr *ifa;
int err;

if (!capable(CAP_NET_ADMIN))
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

if (!net_eq(net, &init_net))
Expand Down
4 changes: 2 additions & 2 deletions net/decnet/dn_fib.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
struct nlattr *attrs[RTA_MAX+1];
int err;

if (!capable(CAP_NET_ADMIN))
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

if (!net_eq(net, &init_net))
Expand All @@ -530,7 +530,7 @@ static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
struct nlattr *attrs[RTA_MAX+1];
int err;

if (!capable(CAP_NET_ADMIN))
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

if (!net_eq(net, &init_net))
Expand Down
2 changes: 1 addition & 1 deletion net/decnet/netfilter/dn_rtmsg.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ static inline void dnrmg_receive_user_skb(struct sk_buff *skb)
if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
return;

if (!capable(CAP_NET_ADMIN))
if (!netlink_capable(skb, CAP_NET_ADMIN))
RCV_SKB_FAIL(-EPERM);

/* Eventually we might send routing messages too */
Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/nfnetlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ static void nfnetlink_rcv(struct sk_buff *skb)
skb->len < nlh->nlmsg_len)
return;

if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) {
if (!netlink_net_capable(skb, CAP_NET_ADMIN)) {
netlink_ack(skb, nlh, -EPERM);
return;
}
Expand Down
2 changes: 1 addition & 1 deletion net/netlink/genetlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ static int genl_family_rcv_msg(struct genl_family *family,
return -EOPNOTSUPP;

if ((ops->flags & GENL_ADMIN_PERM) &&
!capable(CAP_NET_ADMIN))
!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) {
Expand Down
2 changes: 1 addition & 1 deletion net/packet/diag.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ static int packet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)

net = sock_net(skb->sk);
req = nlmsg_data(cb->nlh);
may_report_filterinfo = ns_capable(net->user_ns, CAP_NET_ADMIN);
may_report_filterinfo = netlink_net_capable(cb->skb, CAP_NET_ADMIN);

mutex_lock(&net->packet.sklist_lock);
sk_for_each(sk, &net->packet.sklist) {
Expand Down
8 changes: 4 additions & 4 deletions net/phonet/pn_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh)
int err;
u8 pnaddr;

if (!capable(CAP_NET_ADMIN))
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

if (!capable(CAP_SYS_ADMIN))
if (!netlink_capable(skb, CAP_SYS_ADMIN))
return -EPERM;

ASSERT_RTNL();
Expand Down Expand Up @@ -233,10 +233,10 @@ static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh)
int err;
u8 dst;

if (!capable(CAP_NET_ADMIN))
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

if (!capable(CAP_SYS_ADMIN))
if (!netlink_capable(skb, CAP_SYS_ADMIN))
return -EPERM;

ASSERT_RTNL();
Expand Down
2 changes: 1 addition & 1 deletion net/sched/act_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,7 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n)
u32 portid = skb ? NETLINK_CB(skb).portid : 0;
int ret = 0, ovr = 0;

if ((n->nlmsg_type != RTM_GETACTION) && !capable(CAP_NET_ADMIN))
if ((n->nlmsg_type != RTM_GETACTION) && !netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL);
Expand Down
2 changes: 1 addition & 1 deletion net/sched/cls_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n)
int err;
int tp_created = 0;

if ((n->nlmsg_type != RTM_GETTFILTER) && !capable(CAP_NET_ADMIN))
if ((n->nlmsg_type != RTM_GETTFILTER) && !netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

replay:
Expand Down
6 changes: 3 additions & 3 deletions net/sched/sch_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1084,7 +1084,7 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n)
struct Qdisc *p = NULL;
int err;

if ((n->nlmsg_type != RTM_GETQDISC) && !capable(CAP_NET_ADMIN))
if ((n->nlmsg_type != RTM_GETQDISC) && !netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
Expand Down Expand Up @@ -1151,7 +1151,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n)
struct Qdisc *q, *p;
int err;

if (!capable(CAP_NET_ADMIN))
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

replay:
Expand Down Expand Up @@ -1490,7 +1490,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n)
u32 qid;
int err;

if ((n->nlmsg_type != RTM_GETTCLASS) && !capable(CAP_NET_ADMIN))
if ((n->nlmsg_type != RTM_GETTCLASS) && !netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;

err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, NULL);
Expand Down
2 changes: 1 addition & 1 deletion net/tipc/netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info)
int hdr_space = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN);
u16 cmd;

if ((req_userhdr->cmd & 0xC000) && (!capable(CAP_NET_ADMIN)))
if ((req_userhdr->cmd & 0xC000) && (!netlink_capable(skb, CAP_NET_ADMIN)))
cmd = TIPC_CMD_NOT_NET_ADMIN;
else
cmd = req_userhdr->cmd;
Expand Down
2 changes: 1 addition & 1 deletion net/xfrm/xfrm_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -2377,7 +2377,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
link = &xfrm_dispatch[type];

/* All operations require privileges, even GET */
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
if (!netlink_net_capable(skb, CAP_NET_ADMIN))
return -EPERM;

if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) ||
Expand Down

0 comments on commit 90f62cf

Please sign in to comment.