* [PATCH net v4] rtnetlink: add missing netlink_ns_capable() check for peer netns
@ 2026-03-28 21:33 Nikolaos Gkarlis
2026-03-31 14:43 ` Nikolaos Gkarlis
2026-04-02 2:45 ` Jakub Kicinski
0 siblings, 2 replies; 6+ messages in thread
From: Nikolaos Gkarlis @ 2026-03-28 21:33 UTC (permalink / raw)
To: netdev; +Cc: kuba, nickgarlis, kuniyu
rtnl_newlink() lacks a CAP_NET_ADMIN capability check on the peer
network namespace when creating paired devices (veth, vxcan,
netkit). This allows an unprivileged user with a user namespace
to create interfaces in arbitrary network namespaces, including
init_net.
Add a netlink_ns_capable() check for CAP_NET_ADMIN in the peer
namespace before allowing device creation to proceed.
Fixes: 81adee47dfb6 ("net: Support specifying the network namespace upon device creation.")
Signed-off-by: Nikolaos Gkarlis <nickgarlis@gmail.com>
---
v4:
- Use IS_ERR_OR_NULL instead of IS_ERR + null check.
v3:
- Move netlink_ns_capable() check from rtnl_newlink() into
rtnl_get_peer_net(), after the last rtnl_link_get_net_ifla(tb)
call. The tbp path is already covered by rtnl_link_get_net_capable()
in the caller. (suggested by Kuniyuki)
- Pass skb to rtnl_get_peer_net() for the capability check.
- Add IS_ERR() check on rtnl_link_get_net_ifla(tb) return value.
v2:
- Removed "Reported-by" tag
- Fixed "Fixes" tag with the help of Kuniyuki Iwashima (thanks !)
net/core/rtnetlink.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index fae8034efbf..a4d8fd8232e 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -3894,12 +3894,14 @@ static int rtnl_newlink_create(struct sk_buff *skb, struct ifinfomsg *ifm,
goto out;
}
-static struct net *rtnl_get_peer_net(const struct rtnl_link_ops *ops,
+static struct net *rtnl_get_peer_net(struct sk_buff *skb,
+ const struct rtnl_link_ops *ops,
struct nlattr *tbp[],
struct nlattr *data[],
struct netlink_ext_ack *extack)
{
struct nlattr *tb[IFLA_MAX + 1];
+ struct net *net;
int err;
if (!data || !data[ops->peer_type])
@@ -3915,7 +3917,16 @@ static struct net *rtnl_get_peer_net(const struct rtnl_link_ops *ops,
return ERR_PTR(err);
}
- return rtnl_link_get_net_ifla(tb);
+ net = rtnl_link_get_net_ifla(tb);
+ if (IS_ERR_OR_NULL(net))
+ return net;
+
+ if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
+ put_net(net);
+ return ERR_PTR(-EPERM);
+ }
+
+ return net;
}
static int __rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -4054,7 +4065,7 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh,
}
if (ops->peer_type) {
- peer_net = rtnl_get_peer_net(ops, tb, data, extack);
+ peer_net = rtnl_get_peer_net(skb, ops, tb, data, extack);
if (IS_ERR(peer_net)) {
ret = PTR_ERR(peer_net);
goto put_ops;
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH net v4] rtnetlink: add missing netlink_ns_capable() check for peer netns 2026-03-28 21:33 [PATCH net v4] rtnetlink: add missing netlink_ns_capable() check for peer netns Nikolaos Gkarlis @ 2026-03-31 14:43 ` Nikolaos Gkarlis 2026-04-02 2:45 ` Jakub Kicinski 1 sibling, 0 replies; 6+ messages in thread From: Nikolaos Gkarlis @ 2026-03-31 14:43 UTC (permalink / raw) To: netdev; +Cc: kuba, kuniyu Just following up on this patch; is it okay as is, or does anything need to be addressed? Thanks. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net v4] rtnetlink: add missing netlink_ns_capable() check for peer netns 2026-03-28 21:33 [PATCH net v4] rtnetlink: add missing netlink_ns_capable() check for peer netns Nikolaos Gkarlis 2026-03-31 14:43 ` Nikolaos Gkarlis @ 2026-04-02 2:45 ` Jakub Kicinski 2026-04-02 17:45 ` Nikolaos Gkarlis 1 sibling, 1 reply; 6+ messages in thread From: Jakub Kicinski @ 2026-04-02 2:45 UTC (permalink / raw) To: Nikolaos Gkarlis; +Cc: netdev, kuniyu On Sat, 28 Mar 2026 22:33:38 +0100 Nikolaos Gkarlis wrote: > -static struct net *rtnl_get_peer_net(const struct rtnl_link_ops *ops, > +static struct net *rtnl_get_peer_net(struct sk_buff *skb, > + const struct rtnl_link_ops *ops, > struct nlattr *tbp[], > struct nlattr *data[], > struct netlink_ext_ack *extack) > { > struct nlattr *tb[IFLA_MAX + 1]; > + struct net *net; > int err; > > if (!data || !data[ops->peer_type]) There's an early return hiding outside of the context here. the patch is technically correct, I think, because if we take this shortcut we end up with the same netns as tgt_net so we'll validate that it's capable later. But it's probably not obvious to a casual reader of this code (or AI agents, sigh) So let's rewrite this along the lines of: struct nlattr *tb[IFLA_MAX + 1], **attrs; struct net *net; int err; if (!data || !data[ops->peer_type]) { attrs = tbp; } else { err = rtnl_nla_parse_ifinfomsg(tb, data[ops->peer_type], extack); if (err < 0) return ERR_PTR(err); if (ops->validate) { err = ops->validate(tb, NULL, extack); if (err < 0) return ERR_PTR(err); } attrs = tb; } net = rtnl_link_get_net_ifla(attrs); if (IS_ERR_OR_NULL(net)) return net; if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { ... ? > @@ -3915,7 +3917,16 @@ static struct net *rtnl_get_peer_net(const struct rtnl_link_ops *ops, > return ERR_PTR(err); > } > > - return rtnl_link_get_net_ifla(tb); > + net = rtnl_link_get_net_ifla(tb); > + if (IS_ERR_OR_NULL(net)) > + return net; > + > + if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { > + put_net(net); > + return ERR_PTR(-EPERM); > + } > + > + return net; ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net v4] rtnetlink: add missing netlink_ns_capable() check for peer netns 2026-04-02 2:45 ` Jakub Kicinski @ 2026-04-02 17:45 ` Nikolaos Gkarlis 2026-04-02 17:52 ` Kuniyuki Iwashima 0 siblings, 1 reply; 6+ messages in thread From: Nikolaos Gkarlis @ 2026-04-02 17:45 UTC (permalink / raw) To: Jakub Kicinski; +Cc: netdev, kuniyu On Thu, Apr 2, 2026 at 4:45 AM Jakub Kicinski <kuba@kernel.org> wrote: > > On Sat, 28 Mar 2026 22:33:38 +0100 Nikolaos Gkarlis wrote: > > -static struct net *rtnl_get_peer_net(const struct rtnl_link_ops *ops, > > +static struct net *rtnl_get_peer_net(struct sk_buff *skb, > > + const struct rtnl_link_ops *ops, > > struct nlattr *tbp[], > > struct nlattr *data[], > > struct netlink_ext_ack *extack) > > { > > struct nlattr *tb[IFLA_MAX + 1]; > > + struct net *net; > > int err; > > > > if (!data || !data[ops->peer_type]) > > There's an early return hiding outside of the context here. > the patch is technically correct, I think, because if we take this > shortcut we end up with the same netns as tgt_net so we'll validate > that it's capable later. But it's probably not obvious to a casual > reader of this code (or AI agents, sigh) > > So let's rewrite this along the lines of: > > struct nlattr *tb[IFLA_MAX + 1], **attrs; > struct net *net; > int err; > > if (!data || !data[ops->peer_type]) { > attrs = tbp; > } else { > err = rtnl_nla_parse_ifinfomsg(tb, data[ops->peer_type], extack); > if (err < 0) > return ERR_PTR(err); > > if (ops->validate) { > err = ops->validate(tb, NULL, extack); > if (err < 0) > return ERR_PTR(err); > } > > attrs = tb; > } > > net = rtnl_link_get_net_ifla(attrs); > if (IS_ERR_OR_NULL(net)) > return net; > > if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { > ... > > ? I agree it’s a bit confusing with the early exit. I’ll apply the suggested changes and send a v5. That said, would it make sense to introduce a separate rtnl_nets_add_capable() helper that wraps rtnl_nets_add() instead? Something along these lines: @@ -334,6 +334,19 @@ static void rtnl_nets_add(struct rtnl_nets *rtnl_nets, struct net *net) rtnl_nets->len++; } +static struct net *rtnl_nets_add_capable(struct sk_buff *skb, + struct rtnl_nets *rtnl_nets, + struct net *net) +{ + if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { + put_net(net); + return ERR_PTR(-EPERM); + } + + rtnl_nets_add(rtnl_nets, net); + return net; +} + static void rtnl_nets_lock(struct rtnl_nets *rtnl_nets) { int i; @@ -4059,18 +4072,29 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, ret = PTR_ERR(peer_net); goto put_ops; } - if (peer_net) - rtnl_nets_add(&rtnl_nets, peer_net); + if (peer_net) { + peer_net = rtnl_nets_add_capable(skb, + &rtnl_nets, + peer_net); + if (IS_ERR(peer_net)) { + ret = PTR_ERR(peer_net); + goto put_ops; + } + } } } - tgt_net = rtnl_link_get_net_capable(skb, sock_net(skb->sk), tb, CAP_NET_ADMIN); + tgt_net = rtnl_link_get_net_by_nlattr(sock_net(skb->sk), tb); if (IS_ERR(tgt_net)) { ret = PTR_ERR(tgt_net); goto put_net; } - rtnl_nets_add(&rtnl_nets, tgt_net); + tgt_net = rtnl_nets_add_capable(skb, &rtnl_nets, tgt_net); + if (IS_ERR(tgt_net)) { + ret = PTR_ERR(tgt_net); + goto put_net; + } if (tb[IFLA_LINK_NETNSID]) { int id = nla_get_s32(tb[IFLA_LINK_NETNSID]); @@ -4082,10 +4106,10 @@ static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, goto put_net; } - rtnl_nets_add(&rtnl_nets, link_net); - - if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN)) { - ret = -EPERM; + link_net = rtnl_nets_add_capable(skb, &rtnl_nets, + link_net); + if (IS_ERR(link_net)) { + ret = PTR_ERR(link_net); goto put_net; } } Note that this changes the order in link_net, checking capabilities before creating the object. Disclaimer: I’m not very familiar with this code, so this may be a bad idea. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net v4] rtnetlink: add missing netlink_ns_capable() check for peer netns 2026-04-02 17:45 ` Nikolaos Gkarlis @ 2026-04-02 17:52 ` Kuniyuki Iwashima 2026-04-02 18:17 ` Nikolaos Gkarlis 0 siblings, 1 reply; 6+ messages in thread From: Kuniyuki Iwashima @ 2026-04-02 17:52 UTC (permalink / raw) To: Nikolaos Gkarlis; +Cc: Jakub Kicinski, netdev On Thu, Apr 2, 2026 at 10:46 AM Nikolaos Gkarlis <nickgarlis@gmail.com> wrote: > > On Thu, Apr 2, 2026 at 4:45 AM Jakub Kicinski <kuba@kernel.org> wrote: > > > > On Sat, 28 Mar 2026 22:33:38 +0100 Nikolaos Gkarlis wrote: > > > -static struct net *rtnl_get_peer_net(const struct rtnl_link_ops *ops, > > > +static struct net *rtnl_get_peer_net(struct sk_buff *skb, > > > + const struct rtnl_link_ops *ops, > > > struct nlattr *tbp[], > > > struct nlattr *data[], > > > struct netlink_ext_ack *extack) > > > { > > > struct nlattr *tb[IFLA_MAX + 1]; > > > + struct net *net; > > > int err; > > > > > > if (!data || !data[ops->peer_type]) > > > > There's an early return hiding outside of the context here. > > the patch is technically correct, I think, because if we take this > > shortcut we end up with the same netns as tgt_net so we'll validate > > that it's capable later. But it's probably not obvious to a casual > > reader of this code (or AI agents, sigh) > > > > So let's rewrite this along the lines of: > > > > struct nlattr *tb[IFLA_MAX + 1], **attrs; > > struct net *net; > > int err; > > > > if (!data || !data[ops->peer_type]) { > > attrs = tbp; > > } else { > > err = rtnl_nla_parse_ifinfomsg(tb, data[ops->peer_type], extack); > > if (err < 0) > > return ERR_PTR(err); > > > > if (ops->validate) { > > err = ops->validate(tb, NULL, extack); > > if (err < 0) > > return ERR_PTR(err); > > } > > > > attrs = tb; > > } > > > > net = rtnl_link_get_net_ifla(attrs); > > if (IS_ERR_OR_NULL(net)) > > return net; > > > > if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { > > ... > > > > ? > > I agree it’s a bit confusing with the early exit. I’ll apply the suggested > changes and send a v5. > > That said, would it make sense to introduce a separate > rtnl_nets_add_capable() helper that wraps rtnl_nets_add() instead? Let's focus on the fix in net.git. Also, I don't want to hide rtnl_nets_add() in a helper. > > Something along these lines: > > @@ -334,6 +334,19 @@ static void rtnl_nets_add(struct rtnl_nets > *rtnl_nets, struct net *net) > rtnl_nets->len++; > } > > +static struct net *rtnl_nets_add_capable(struct sk_buff *skb, > + struct rtnl_nets *rtnl_nets, > + struct net *net) > +{ > + if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { > + put_net(net); > + return ERR_PTR(-EPERM); > + } > + > + rtnl_nets_add(rtnl_nets, net); > + return net; > +} > + > static void rtnl_nets_lock(struct rtnl_nets *rtnl_nets) > { > int i; > @@ -4059,18 +4072,29 @@ static int rtnl_newlink(struct sk_buff *skb, > struct nlmsghdr *nlh, > ret = PTR_ERR(peer_net); > goto put_ops; > } > - if (peer_net) > - rtnl_nets_add(&rtnl_nets, peer_net); > + if (peer_net) { > + peer_net = rtnl_nets_add_capable(skb, > + &rtnl_nets, > + peer_net); > + if (IS_ERR(peer_net)) { > + ret = PTR_ERR(peer_net); > + goto put_ops; > + } > + } > } > } > > - tgt_net = rtnl_link_get_net_capable(skb, sock_net(skb->sk), tb, > CAP_NET_ADMIN); > + tgt_net = rtnl_link_get_net_by_nlattr(sock_net(skb->sk), tb); > if (IS_ERR(tgt_net)) { > ret = PTR_ERR(tgt_net); > goto put_net; > } > > - rtnl_nets_add(&rtnl_nets, tgt_net); > + tgt_net = rtnl_nets_add_capable(skb, &rtnl_nets, tgt_net); > + if (IS_ERR(tgt_net)) { > + ret = PTR_ERR(tgt_net); > + goto put_net; > + } > > if (tb[IFLA_LINK_NETNSID]) { > int id = nla_get_s32(tb[IFLA_LINK_NETNSID]); > @@ -4082,10 +4106,10 @@ static int rtnl_newlink(struct sk_buff *skb, > struct nlmsghdr *nlh, > goto put_net; > } > > - rtnl_nets_add(&rtnl_nets, link_net); > - > - if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN)) { > - ret = -EPERM; > + link_net = rtnl_nets_add_capable(skb, &rtnl_nets, > + link_net); > + if (IS_ERR(link_net)) { > + ret = PTR_ERR(link_net); > goto put_net; > } > } > > Note that this changes the order in link_net, checking capabilities > before creating the object. > > Disclaimer: I’m not very familiar with this code, so this may be a bad idea. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net v4] rtnetlink: add missing netlink_ns_capable() check for peer netns 2026-04-02 17:52 ` Kuniyuki Iwashima @ 2026-04-02 18:17 ` Nikolaos Gkarlis 0 siblings, 0 replies; 6+ messages in thread From: Nikolaos Gkarlis @ 2026-04-02 18:17 UTC (permalink / raw) To: Kuniyuki Iwashima; +Cc: Jakub Kicinski, netdev > Let's focus on the fix in net.git. > Also, I don't want to hide rtnl_nets_add() in a helper. I've now submitted v5. Thanks for reviewing, and apologies for the detour. ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-04-02 18:17 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-03-28 21:33 [PATCH net v4] rtnetlink: add missing netlink_ns_capable() check for peer netns Nikolaos Gkarlis 2026-03-31 14:43 ` Nikolaos Gkarlis 2026-04-02 2:45 ` Jakub Kicinski 2026-04-02 17:45 ` Nikolaos Gkarlis 2026-04-02 17:52 ` Kuniyuki Iwashima 2026-04-02 18:17 ` Nikolaos Gkarlis
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox