Netlink messages sent by xfrm differ in size between 64-bit native and 32-bit compatible applications. To know which UABI to use to send the message from kernel, I'll use the type of bind() syscall. Xfrm will have hidden from userspace kernel-only groups for compatible applications. So, add pointer to groups to netlink_bind(). With later patches xfrm will set a proper compat group for netlink socket during bind(). Cc: "David S. Miller" <davem@xxxxxxxxxxxxx> Cc: Eric Paris <eparis@xxxxxxxxxx> Cc: Florian Westphal <fw@xxxxxxxxx> Cc: Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx> Cc: Jozsef Kadlecsik <kadlec@xxxxxxxxxxxxxxxxx> Cc: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx> Cc: Paul Moore <paul@xxxxxxxxxxxxxx> Cc: Steffen Klassert <steffen.klassert@xxxxxxxxxxx> Cc: coreteam@xxxxxxxxxxxxx Cc: linux-audit@xxxxxxxxxx Cc: netdev@xxxxxxxxxxxxxxx Cc: netfilter-devel@xxxxxxxxxxxxxxx Signed-off-by: Dmitry Safonov <dima@xxxxxxxxxx> --- include/linux/netlink.h | 2 +- kernel/audit.c | 2 +- net/core/rtnetlink.c | 14 ++++++-------- net/core/sock_diag.c | 25 ++++++++++++------------- net/netfilter/nfnetlink.c | 24 ++++++++++++++---------- net/netlink/af_netlink.c | 27 ++++++++++----------------- net/netlink/af_netlink.h | 4 ++-- net/netlink/genetlink.c | 26 ++++++++++++++++++-------- 8 files changed, 64 insertions(+), 60 deletions(-) diff --git a/include/linux/netlink.h b/include/linux/netlink.h index f3075d6c7e82..19202648e04a 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -46,7 +46,7 @@ struct netlink_kernel_cfg { unsigned int flags; void (*input)(struct sk_buff *skb); struct mutex *cb_mutex; - int (*bind)(struct net *net, int group); + int (*bind)(struct net *net, unsigned long *groups); void (*unbind)(struct net *net, int group); bool (*compare)(struct net *net, struct sock *sk); }; diff --git a/kernel/audit.c b/kernel/audit.c index e7478cb58079..87ca0214bcf2 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1523,7 +1523,7 @@ static void audit_receive(struct sk_buff *skb) } /* Run custom bind function on netlink socket group connect or bind requests. */ -static int audit_bind(struct net *net, int group) +static int audit_bind(struct net *net, unsigned long *groups) { if (!capable(CAP_AUDIT_READ)) return -EPERM; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index e3f743c141b3..0465e692ae32 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4683,15 +4683,13 @@ static void rtnetlink_rcv(struct sk_buff *skb) netlink_rcv_skb(skb, &rtnetlink_rcv_msg); } -static int rtnetlink_bind(struct net *net, int group) +static int rtnetlink_bind(struct net *net, unsigned long *groups) { - switch (group) { - case RTNLGRP_IPV4_MROUTE_R: - case RTNLGRP_IPV6_MROUTE_R: - if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) - return -EPERM; - break; - } + unsigned long mroute_r; + + mroute_r = 1UL << RTNLGRP_IPV4_MROUTE_R | 1UL << RTNLGRP_IPV6_MROUTE_R; + if ((*groups & mroute_r) && !ns_capable(net->user_ns, CAP_NET_ADMIN)) + return -EPERM; return 0; } diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c index c37b5be7c5e4..befa6759f2ad 100644 --- a/net/core/sock_diag.c +++ b/net/core/sock_diag.c @@ -273,20 +273,19 @@ static void sock_diag_rcv(struct sk_buff *skb) mutex_unlock(&sock_diag_mutex); } -static int sock_diag_bind(struct net *net, int group) +static int sock_diag_bind(struct net *net, unsigned long *groups) { - switch (group) { - case SKNLGRP_INET_TCP_DESTROY: - case SKNLGRP_INET_UDP_DESTROY: - if (!sock_diag_handlers[AF_INET]) - sock_load_diag_module(AF_INET, 0); - break; - case SKNLGRP_INET6_TCP_DESTROY: - case SKNLGRP_INET6_UDP_DESTROY: - if (!sock_diag_handlers[AF_INET6]) - sock_load_diag_module(AF_INET6, 0); - break; - } + unsigned long inet_mask, inet6_mask; + + inet_mask = 1UL << SKNLGRP_INET_TCP_DESTROY; + inet_mask |= 1UL << SKNLGRP_INET_UDP_DESTROY; + inet6_mask = 1UL << SKNLGRP_INET6_TCP_DESTROY; + inet6_mask |= 1UL << SKNLGRP_INET6_UDP_DESTROY; + + if ((*groups & inet_mask) && !sock_diag_handlers[AF_INET]) + sock_load_diag_module(AF_INET, 0); + if ((*groups & inet6_mask) && !sock_diag_handlers[AF_INET6]) + sock_load_diag_module(AF_INET6, 0); return 0; } diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index e1b6be29848d..6a8893df5285 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -556,21 +556,25 @@ static void nfnetlink_rcv(struct sk_buff *skb) } #ifdef CONFIG_MODULES -static int nfnetlink_bind(struct net *net, int group) +static int nfnetlink_bind(struct net *net, unsigned long *groups) { const struct nfnetlink_subsystem *ss; - int type; + unsigned long _groups = *groups; + int type, group_bit, group = -1; - if (group <= NFNLGRP_NONE || group > NFNLGRP_MAX) - return 0; + while ((group_bit = __builtin_ffsl(_groups))) { + group += group_bit; - type = nfnl_group2type[group]; + type = nfnl_group2type[group]; + rcu_read_lock(); + ss = nfnetlink_get_subsys(type << 8); + rcu_read_unlock(); + if (!ss) + request_module("nfnetlink-subsys-%d", type); + + _groups >>= group_bit; + } - rcu_read_lock(); - ss = nfnetlink_get_subsys(type << 8); - rcu_read_unlock(); - if (!ss) - request_module("nfnetlink-subsys-%d", type); return 0; } #endif diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index ac805caed2e2..1e11e706c683 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -668,7 +668,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol, struct module *module = NULL; struct mutex *cb_mutex; struct netlink_sock *nlk; - int (*bind)(struct net *net, int group); + int (*bind)(struct net *net, unsigned long *groups); void (*unbind)(struct net *net, int group); int err = 0; @@ -969,8 +969,7 @@ static int netlink_realloc_groups(struct sock *sk) return err; } -static void netlink_undo_bind(int group, long unsigned int groups, - struct sock *sk) +static void netlink_undo_bind(unsigned long groups, struct sock *sk) { struct netlink_sock *nlk = nlk_sk(sk); int undo; @@ -978,7 +977,7 @@ static void netlink_undo_bind(int group, long unsigned int groups, if (!nlk->netlink_unbind) return; - for (undo = 0; undo < group; undo++) + for (undo = 0; undo < nlk->ngroups; undo++) if (test_bit(undo, &groups)) nlk->netlink_unbind(sock_net(sk), undo + 1); } @@ -991,7 +990,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, struct netlink_sock *nlk = nlk_sk(sk); struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; int err = 0; - long unsigned int groups = nladdr->nl_groups; + unsigned long groups = nladdr->nl_groups; bool bound; if (addr_len < sizeof(struct sockaddr_nl)) @@ -1021,17 +1020,9 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, netlink_lock_table(); if (nlk->netlink_bind && groups) { - int group; - - for (group = 0; group < nlk->ngroups; group++) { - if (!test_bit(group, &groups)) - continue; - err = nlk->netlink_bind(net, group + 1); - if (!err) - continue; - netlink_undo_bind(group, groups, sk); + err = nlk->netlink_bind(net, &groups); + if (err) goto unlock; - } } /* No need for barriers here as we return to user-space without @@ -1042,7 +1033,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, netlink_insert(sk, nladdr->nl_pid) : netlink_autobind(sock); if (err) { - netlink_undo_bind(nlk->ngroups, groups, sk); + netlink_undo_bind(groups, sk); goto unlock; } } @@ -1652,7 +1643,9 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, if (!val || val - 1 >= nlk->ngroups) return -EINVAL; if (optname == NETLINK_ADD_MEMBERSHIP && nlk->netlink_bind) { - err = nlk->netlink_bind(sock_net(sk), val); + unsigned long groups = 1UL << val; + + err = nlk->netlink_bind(sock_net(sk), &groups); if (err) return err; } diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h index 962de7b3c023..e765172abbb7 100644 --- a/net/netlink/af_netlink.h +++ b/net/netlink/af_netlink.h @@ -39,7 +39,7 @@ struct netlink_sock { struct mutex *cb_mutex; struct mutex cb_def_mutex; void (*netlink_rcv)(struct sk_buff *skb); - int (*netlink_bind)(struct net *net, int group); + int (*netlink_bind)(struct net *net, unsigned long *groups); void (*netlink_unbind)(struct net *net, int group); struct module *module; @@ -61,7 +61,7 @@ struct netlink_table { unsigned int groups; struct mutex *cb_mutex; struct module *module; - int (*bind)(struct net *net, int group); + int (*bind)(struct net *net, unsigned long *groups); void (*unbind)(struct net *net, int group); bool (*compare)(struct net *net, struct sock *sock); int registered; diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index 25eeb6d2a75a..a86b105730cf 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -960,28 +960,38 @@ static struct genl_family genl_ctrl __ro_after_init = { .netnsok = true, }; -static int genl_bind(struct net *net, int group) +static int genl_bind(struct net *net, unsigned long *groups) { + unsigned long mcgrps; struct genl_family *f; - int err = -ENOENT; + int err = 0; unsigned int id; down_read(&cb_lock); idr_for_each_entry(&genl_fam_idr, f, id) { - if (group >= f->mcgrp_offset && - group < f->mcgrp_offset + f->n_mcgrps) { - int fam_grp = group - f->mcgrp_offset; + int fam_grp_bit, fam_grp = -1; + + mcgrps = (1UL << f->n_mcgrps) - 1; + mcgrps <<= f->mcgrp_offset; + mcgrps &= *groups; + + if (!mcgrps) + continue; + + while ((fam_grp_bit = __builtin_ffsl(mcgrps))) { + fam_grp += fam_grp_bit; if (!f->netnsok && net != &init_net) err = -ENOENT; else if (f->mcast_bind) err = f->mcast_bind(net, fam_grp); - else - err = 0; - break; + + if (err) + goto out; } } +out: up_read(&cb_lock); return err; -- 2.13.6 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html