wireguard.lists.zx2c4.com archive mirror
 help / color / mirror / Atom feed
From: Julian Orth <ju.orth@gmail.com>
To: wireguard@lists.zx2c4.com
Subject: [PATCH v2 09/10] netlink: allow bypassing CAP_NET_ADMIN
Date: Sun,  9 Sep 2018 17:14:01 +0200	[thread overview]
Message-ID: <20180909151402.6033-10-ju.orth@gmail.com> (raw)
In-Reply-To: <20180909151402.6033-1-ju.orth@gmail.com>

Two new attributes have been added:

* WGDEVICE_A_TRANSIT_CREDENTIALS_IPV4
* WGDEVICE_A_TRANSIT_CREDENTIALS_IPV6

If present, they have to refer to an IPv4 (resp. IPv6) UDP socket file
descriptor in the transit namespace. If the IPv6 fd is present, then the
IPv4 fd must also be present. If the IPv4 fd is present and the kernel
was configured with IPv6 support, then the IPv6 fd must also be present.

If they are present and these conditions are fulfilled, then changing
the listen-port or transit-netns does not require CAP_NET_ADMIN in the
transit namespace.
---
 src/netlink.c        | 74 +++++++++++++++++++++++++++++++++++++-------
 src/uapi/wireguard.h | 10 +++++-
 2 files changed, 71 insertions(+), 13 deletions(-)

diff --git a/src/netlink.c b/src/netlink.c
index e7f8c69..7eeb36d 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -10,6 +10,7 @@
 #include "queueing.h"
 #include "messages.h"
 #include "uapi/wireguard.h"
+#include <linux/file.h>
 #include <linux/if.h>
 #include <net/genetlink.h>
 #include <net/sock.h>
@@ -17,16 +18,18 @@
 static struct genl_family genl_family;
 
 static const struct nla_policy device_policy[WGDEVICE_A_MAX + 1] = {
-	[WGDEVICE_A_IFINDEX]		= { .type = NLA_U32 },
-	[WGDEVICE_A_IFNAME]		= { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
-	[WGDEVICE_A_PRIVATE_KEY]	= { .len = NOISE_PUBLIC_KEY_LEN },
-	[WGDEVICE_A_PUBLIC_KEY]		= { .len = NOISE_PUBLIC_KEY_LEN },
-	[WGDEVICE_A_FLAGS]		= { .type = NLA_U32 },
-	[WGDEVICE_A_LISTEN_PORT]	= { .type = NLA_U16 },
-	[WGDEVICE_A_FWMARK]		= { .type = NLA_U32 },
-	[WGDEVICE_A_PEERS]		= { .type = NLA_NESTED },
-	[WGDEVICE_A_TRANSIT_NETNS_PID]	= { .type = NLA_U32 },
-	[WGDEVICE_A_TRANSIT_NETNS_FD]	= { .type = NLA_U32 },
+	[WGDEVICE_A_IFINDEX]			= { .type = NLA_U32 },
+	[WGDEVICE_A_IFNAME]			= { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
+	[WGDEVICE_A_PRIVATE_KEY]		= { .len = NOISE_PUBLIC_KEY_LEN },
+	[WGDEVICE_A_PUBLIC_KEY]			= { .len = NOISE_PUBLIC_KEY_LEN },
+	[WGDEVICE_A_FLAGS]			= { .type = NLA_U32 },
+	[WGDEVICE_A_LISTEN_PORT]		= { .type = NLA_U16 },
+	[WGDEVICE_A_FWMARK]			= { .type = NLA_U32 },
+	[WGDEVICE_A_PEERS]			= { .type = NLA_NESTED },
+	[WGDEVICE_A_TRANSIT_NETNS_PID]		= { .type = NLA_U32 },
+	[WGDEVICE_A_TRANSIT_NETNS_FD]		= { .type = NLA_U32 },
+	[WGDEVICE_A_TRANSIT_CREDENTIALS_IPV4]	= { .type = NLA_U32 },
+	[WGDEVICE_A_TRANSIT_CREDENTIALS_IPV6]	= { .type = NLA_U32 },
 };
 
 static const struct nla_policy peer_policy[WGPEER_A_MAX + 1] = {
@@ -304,8 +307,55 @@ static int get_device_done(struct netlink_callback *cb)
 	return 0;
 }
 
-static int test_net_capable(struct net *net)
+static int test_net_capable_with_creds(struct net *net,
+		struct nlattr *ipv4_attr, struct nlattr *ipv6_attr)
 {
+	struct socket *ipv4 = NULL, *ipv6 = NULL;
+	int ret = -EINVAL;
+
+	ipv4 = sockfd_lookup(nla_get_u32(ipv4_attr), &ret);
+	if (!ipv4)
+		goto out;
+	if (ipv4->type != SOCK_DGRAM)
+		goto out;
+	if (ipv4->ops->family != AF_INET)
+		goto out;
+	if (sock_net(ipv4->sk) != net)
+		goto out;
+
+#if IS_ENABLED(CONFIG_IPV6)
+	ipv6 = sockfd_lookup(nla_get_u32(ipv6_attr), &ret);
+	if (!ipv6)
+		goto out;
+	if (ipv6->type != SOCK_DGRAM)
+		goto out;
+	if (ipv6->ops->family != AF_INET6)
+		goto out;
+	if (sock_net(ipv6->sk) != net)
+		goto out;
+#endif
+
+	ret = 0;
+out:
+	if (ipv4)
+		sockfd_put(ipv4);
+	if (ipv6)
+		sockfd_put(ipv6);
+	return ret;
+}
+
+static int test_net_capable(struct net *net, struct nlattr **attrs)
+{
+	struct nlattr *ipv4_attr = attrs[WGDEVICE_A_TRANSIT_CREDENTIALS_IPV4];
+	struct nlattr *ipv6_attr = attrs[WGDEVICE_A_TRANSIT_CREDENTIALS_IPV6];
+
+	if (ipv4_attr && !ipv6_attr && IS_ENABLED(CONFIG_IPV6))
+		return -EINVAL;
+	if (!ipv4_attr && ipv6_attr)
+		return -EINVAL;
+	if (ipv4_attr)
+		return test_net_capable_with_creds(net, ipv4_attr, ipv6_attr);
+
 	if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
 		return -EPERM;
 	return 0;
@@ -336,7 +386,7 @@ static int set_socket(struct wireguard_device *wg, struct nlattr **attrs)
 	else
 		port = wg->incoming_port;
 
-	ret = test_net_capable(net ? : wg->transit_net);
+	ret = test_net_capable(net ? : wg->transit_net, attrs);
 	if (ret)
 		goto out;
 
diff --git a/src/uapi/wireguard.h b/src/uapi/wireguard.h
index 40d800f..3be7def 100644
--- a/src/uapi/wireguard.h
+++ b/src/uapi/wireguard.h
@@ -79,7 +79,11 @@
  *
  * If WGDEVICE_A_TRANSIT_NETNS_PID/FD and/or WGDEVICE_A_LISTEN_PORT are
  * provided, then the calling process must have CAP_NET_ADMIN the transit
- * namespace.
+ * namespace. This requirement is waived if both
+ * WGDEVICE_A_TRANSIT_CREDENTIALS_IPV4 and WGDEVICE_A_TRANSIT_CREDENTIALS_IPV6
+ * are provided and refer to IPv4 (resp. IPv6) UDP sockets in the transit
+ * namespace. (If the kernel is configured without IPv6 support, then
+ * WGDEVICE_A_TRANSIT_CREDENTIALS_IPV6 is not necessary.)
  *
  *
  *    WGDEVICE_A_IFINDEX: NLA_U32
@@ -90,6 +94,8 @@
  *    WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly
  *    WGDEVICE_A_TRANSIT_NETNS_PID: NLA_U32
  *    WGDEVICE_A_TRANSIT_NETNS_FD: NLA_U32
+ *    WGDEVICE_A_TRANSIT_CREDENTIALS_IPV4: NLA_U32
+ *    WGDEVICE_A_TRANSIT_CREDENTIALS_IPV6: NLA_U32
  *    WGDEVICE_A_FWMARK: NLA_U32, 0 to disable
  *    WGDEVICE_A_PEERS: NLA_NESTED
  *        0: NLA_NESTED
@@ -164,6 +170,8 @@ enum wgdevice_attribute {
 	WGDEVICE_A_PEERS,
 	WGDEVICE_A_TRANSIT_NETNS_PID,
 	WGDEVICE_A_TRANSIT_NETNS_FD,
+	WGDEVICE_A_TRANSIT_CREDENTIALS_IPV4,
+	WGDEVICE_A_TRANSIT_CREDENTIALS_IPV6,
 	__WGDEVICE_A_LAST
 };
 #define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1)
-- 
2.18.0

  parent reply	other threads:[~2018-09-09 15:13 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-09-09 15:13 [PATCH v2 00/10] Allow changing the transit namespace Julian Orth
2018-09-09 15:13 ` [PATCH v2 01/10] device: protect socket_init with device_update_lock Julian Orth
2018-09-09 15:13 ` [PATCH v2 02/10] device: rename creating_net to transit_net Julian Orth
2018-09-09 15:13 ` [PATCH v2 03/10] device: store a copy of the device net Julian Orth
2018-09-09 15:13 ` [PATCH v2 04/10] socket: allow modification of transit_net Julian Orth
2018-09-09 15:13 ` [PATCH v2 05/10] netlink: allow modification of transit net Julian Orth
2018-09-09 15:13 ` [PATCH v2 06/10] tools: " Julian Orth
2018-09-09 15:13 ` [PATCH v2 07/10] tests: add test for transit-net Julian Orth
2018-09-09 15:14 ` [PATCH v2 08/10] netlink: require CAP_NET_ADMIN for socket changes Julian Orth
2018-09-09 15:14 ` Julian Orth [this message]
2018-09-09 15:14 ` [PATCH v2 10/10] tools: add support for transit-credentials Julian Orth

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180909151402.6033-10-ju.orth@gmail.com \
    --to=ju.orth@gmail.com \
    --cc=wireguard@lists.zx2c4.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).