lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Thu, 21 Mar 2019 14:41:15 +0100 (CET)
From:   Michal Kubecek <mkubecek@...e.cz>
To:     David Miller <davem@...emloft.net>, netdev@...r.kernel.org
Cc:     Jakub Kicinski <jakub.kicinski@...ronome.com>,
        Jiri Pirko <jiri@...nulli.us>, Andrew Lunn <andrew@...n.ch>,
        Florian Fainelli <f.fainelli@...il.com>,
        John Linville <linville@...driver.com>,
        linux-kernel@...r.kernel.org
Subject: [PATCH net-next v4 19/22] ethtool: provide WoL information in
 GET_SETTINGS request

Add information about supported and enabled wake on LAN modes into the
GET_SETTINGS reply when ETH_SETTINGS_IM_WOL flag is set in the request.

The GET_SETTINGS request can be still sent by unprivileged users but in
such case the SecureOn password (if any) is not included in the reply.

Send notification in the same format as reply SET_SETTINGS message when
wake on LAN settings are modified using ioctl interface (ETHTOOL_SWOL
command).

Signed-off-by: Michal Kubecek <mkubecek@...e.cz>
---
 Documentation/networking/ethtool-netlink.txt | 13 +++-
 include/uapi/linux/ethtool_netlink.h         | 14 ++++-
 net/ethtool/common.c                         | 10 ++++
 net/ethtool/common.h                         |  1 +
 net/ethtool/ioctl.c                          | 10 ++--
 net/ethtool/settings.c                       | 63 ++++++++++++++++++++
 6 files changed, 105 insertions(+), 6 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.txt b/Documentation/networking/ethtool-netlink.txt
index ebd1a0404828..bc7f28f0182f 100644
--- a/Documentation/networking/ethtool-netlink.txt
+++ b/Documentation/networking/ethtool-netlink.txt
@@ -278,6 +278,7 @@ Info mask bits meaning:
     ETH_SETTINGS_IM_LINKINFO		link settings
     ETH_SETTINGS_IM_LINKMODES		link modes and related
     ETH_SETTINGS_IM_LINKSTATE		link state
+    ETH_SETTINGS_IM_WOL			struct ethtool_wolinfo
 
 Response contents:
 
@@ -296,12 +297,22 @@ Response contents:
         ETHA_LINKMODES_DUPLEX		(u8)		duplex mode
     ETHA_SETTINGS_LINK_STATE	(nested)	link state
         ETHA_LINKSTATE_LINK		(u8)		link on/off/unknown
+    ETHA_SETTINGS_WOL		(nested)	wake on LAN settings
+        ETHA_WOL_MODES			(bitfield32)	wake on LAN modes
+        ETHA_WOL_SOPASS			(binary)	SecureOn(tm) password
 
 Most of the attributes and their values have the same meaning as matching
 members of the corresponding ioctl structures. For ETHA_LINKMODES_OURS,
 value represents advertised modes and mask represents supported modes.
 ETHA_LINKMODES_PEER in the reply is a bit list.
 
+For ETHA_WOL_MODES, selector reports wake on LAN modes supported by the
+device and value enabled modes.
+
+GET_SETTINGS request is allowed for unprivileged user but ETHA_WOL_SOPASS
+is only provided by kernel in response to privileged (netns CAP_NET_ADMIN)
+requests.
+
 GET_SETTINGS requests allow dumps and messages in the same format as response
 to them are broadcasted as notifications on change of these settings using
 netlink or ioctl ethtool interface.
@@ -348,7 +359,7 @@ ETHTOOL_GSET			ETHNL_CMD_GET_SETTINGS
 ETHTOOL_SSET			ETHNL_CMD_SET_SETTINGS
 ETHTOOL_GDRVINFO		ETHNL_CMD_GET_INFO
 ETHTOOL_GREGS			n/a
-ETHTOOL_GWOL			n/a
+ETHTOOL_GWOL			ETHNL_CMD_GET_SETTINGS
 ETHTOOL_SWOL			n/a
 ETHTOOL_GMSGLVL			n/a
 ETHTOOL_SMSGLVL			n/a
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index 45c27c7291d0..532ad11ae87c 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -197,6 +197,7 @@ enum {
 	ETHA_SETTINGS_LINK_INFO,		/* nest - ETHA_LINKINFO_* */
 	ETHA_SETTINGS_LINK_MODES,		/* nest - ETHA_LINKMODES_* */
 	ETHA_SETTINGS_LINK_STATE,		/* nest - ETHA_LINKSTATE_* */
+	ETHA_SETTINGS_WOL,			/* nest - ETHA_WOL_* */
 
 	__ETHA_SETTINGS_CNT,
 	ETHA_SETTINGS_MAX = (__ETHA_SETTINGS_CNT - 1)
@@ -205,10 +206,12 @@ enum {
 #define ETH_SETTINGS_IM_LINKINFO		(1U << 0)
 #define ETH_SETTINGS_IM_LINKMODES		(1U << 1)
 #define ETH_SETTINGS_IM_LINKSTATE		(1U << 2)
+#define ETH_SETTINGS_IM_WOL			(1U << 3)
 
 #define ETH_SETTINGS_IM_ALL (ETH_SETTINGS_IM_LINKINFO | \
 			     ETH_SETTINGS_IM_LINKMODES | \
-			     ETH_SETTINGS_IM_LINKSTATE)
+			     ETH_SETTINGS_IM_LINKSTATE | \
+			     ETH_SETTINGS_IM_WOL)
 
 enum {
 	ETHA_LINKINFO_UNSPEC,
@@ -242,6 +245,15 @@ enum {
 	ETHA_LINKSTATE_MAX = (__ETHA_LINKSTATE_CNT - 1)
 };
 
+enum {
+	ETHA_WOL_UNSPEC,
+	ETHA_WOL_MODES,				/* bitfield32 */
+	ETHA_WOL_SOPASS,			/* binary */
+
+	__ETHA_WOL_CNT,
+	ETHA_WOL_MAX = (__ETHA_WOL_CNT - 1)
+};
+
 /* generic netlink info */
 #define ETHTOOL_GENL_NAME "ethtool"
 #define ETHTOOL_GENL_VERSION 1
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index dc907d8b6e43..4b2f08da910a 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -213,3 +213,13 @@ int __ethtool_get_link(struct net_device *dev)
 
 	return netif_running(dev) && dev->ethtool_ops->get_link(dev);
 }
+
+int __ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	if (!dev->ethtool_ops->get_wol)
+		return -EOPNOTSUPP;
+
+	dev->ethtool_ops->get_wol(dev, wol);
+
+	return 0;
+}
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index a5ddd7f5cfce..bbe3e51f7308 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -18,6 +18,7 @@ phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN];
 int __ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
 int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info);
 int __ethtool_get_link(struct net_device *dev);
+int __ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol);
 
 bool convert_legacy_settings_to_link_ksettings(
 	struct ethtool_link_ksettings *link_ksettings,
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 58669dafeaf9..bba02a218eea 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -1242,11 +1242,11 @@ static int ethtool_reset(struct net_device *dev, char __user *useraddr)
 static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
 {
 	struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+	int rc;
 
-	if (!dev->ethtool_ops->get_wol)
-		return -EOPNOTSUPP;
-
-	dev->ethtool_ops->get_wol(dev, &wol);
+	rc = __ethtool_get_wol(dev, &wol);
+	if (rc < 0)
+		return rc;
 
 	if (copy_to_user(useraddr, &wol, sizeof(wol)))
 		return -EFAULT;
@@ -1269,6 +1269,8 @@ static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
 		return ret;
 
 	dev->wol_enabled = !!wol.wolopts;
+	ethtool_notify(dev, NULL, ETHNL_CMD_SET_SETTINGS, ETH_SETTINGS_IM_WOL,
+		       NULL);
 
 	return 0;
 }
diff --git a/net/ethtool/settings.c b/net/ethtool/settings.c
index 7a2729f01f79..56a045e5e916 100644
--- a/net/ethtool/settings.c
+++ b/net/ethtool/settings.c
@@ -6,10 +6,12 @@
 
 struct settings_data {
 	struct common_req_info		reqinfo_base;
+	bool				privileged;
 
 	/* everything below here will be reset for each device in dumps */
 	struct common_reply_data	repdata_base;
 	struct ethtool_link_ksettings	ksettings;
+	struct ethtool_wolinfo		wolinfo;
 	struct ethtool_link_settings	*lsettings;
 	int				link;
 	bool				lpm_empty;
@@ -114,15 +116,20 @@ static const struct nla_policy get_settings_policy[ETHA_SETTINGS_MAX + 1] = {
 	[ETHA_SETTINGS_LINK_INFO]	= { .type = NLA_REJECT },
 	[ETHA_SETTINGS_LINK_MODES]	= { .type = NLA_REJECT },
 	[ETHA_SETTINGS_LINK_STATE]	= { .type = NLA_REJECT },
+	[ETHA_SETTINGS_WOL]		= { .type = NLA_REJECT },
 };
 
 static int parse_settings(struct common_req_info *req_info,
 			  struct sk_buff *skb, struct genl_info *info,
 			  const struct nlmsghdr *nlhdr)
 {
+	struct settings_data *data =
+		container_of(req_info, struct settings_data, reqinfo_base);
 	struct nlattr *tb[ETHA_SETTINGS_MAX + 1];
 	int ret;
 
+	data->privileged = ethnl_is_privileged(skb);
+
 	ret = ethnlmsg_parse(nlhdr, tb, ETHA_SETTINGS_MAX, get_settings_policy,
 			     info);
 	if (ret < 0)
@@ -159,6 +166,16 @@ static int ethnl_get_link_ksettings(struct genl_info *info,
 	return ret;
 }
 
+static int ethnl_get_wol(struct genl_info *info, struct net_device *dev,
+			 struct ethtool_wolinfo *wolinfo)
+{
+	int ret = __ethtool_get_wol(dev, wolinfo);
+
+	if (ret < 0)
+		ETHNL_SET_ERRMSG(info, "failed to retrieve wol info");
+	return ret;
+}
+
 static int prepare_settings(struct common_req_info *req_info,
 			    struct genl_info *info)
 {
@@ -194,6 +211,11 @@ static int prepare_settings(struct common_req_info *req_info,
 	}
 	if (req_mask & ETH_SETTINGS_IM_LINKSTATE)
 		data->link = __ethtool_get_link(dev);
+	if (req_mask & ETH_SETTINGS_IM_WOL) {
+		ret = ethnl_get_wol(info, dev, &data->wolinfo);
+		if (ret < 0)
+			req_mask &= ~ETH_SETTINGS_IM_WOL;
+	}
 	ethnl_after_ops(dev);
 
 	data->repdata_base.info_mask = req_mask;
@@ -249,6 +271,12 @@ static int link_state_size(int link)
 	return nla_total_size(nla_total_size(sizeof(u8)));
 }
 
+static int wol_size(void)
+{
+	return nla_total_size(nla_total_size(sizeof(struct nla_bitfield32)) +
+			      nla_total_size(SOPASS_MAX));
+}
+
 /* To keep things simple, reserve space for some attributes which may not
  * be added to the message (e.g. ETHA_SETTINGS_SOPASS); therefore the length
  * returned may be bigger than the actual length of the message sent
@@ -272,6 +300,8 @@ static int settings_size(const struct common_req_info *req_info)
 	}
 	if (info_mask & ETH_SETTINGS_IM_LINKSTATE)
 		len += link_state_size(data->link);
+	if (info_mask & ETH_SETTINGS_IM_WOL)
+		len += wol_size();
 
 	return len;
 }
@@ -361,6 +391,33 @@ static int fill_link_state(struct sk_buff *skb, u8 link)
 	return -EMSGSIZE;
 }
 
+static int fill_wolinfo(struct sk_buff *skb,
+			const struct ethtool_wolinfo *wolinfo, bool privileged)
+{
+	struct nlattr *nest;
+
+	nest = ethnl_nest_start(skb, ETHA_SETTINGS_WOL);
+	if (!nest)
+		return -EMSGSIZE;
+	if (nla_put_bitfield32(skb, ETHA_WOL_MODES, wolinfo->wolopts,
+			       wolinfo->supported))
+		goto err;
+	/* ioctl() restricts read access to wolinfo but the actual
+	 * reason is to hide sopass from unprivileged users; netlink
+	 * can show wol modes without sopass
+	 */
+	if (privileged &&
+	    nla_put(skb, ETHA_WOL_SOPASS, sizeof(wolinfo->sopass),
+		    wolinfo->sopass))
+		goto err;
+	nla_nest_end(skb, nest);
+	return 0;
+
+err:
+	nla_nest_cancel(skb, nest);
+	return -EMSGSIZE;
+}
+
 static int fill_settings(struct sk_buff *skb,
 			 const struct common_req_info *req_info)
 {
@@ -386,6 +443,11 @@ static int fill_settings(struct sk_buff *skb,
 		if (ret < 0)
 			return ret;
 	}
+	if (info_mask & ETH_SETTINGS_IM_WOL) {
+		ret = fill_wolinfo(skb, &data->wolinfo, data->privileged);
+		if (ret < 0)
+			return ret;
+	}
 
 	return 0;
 }
@@ -431,6 +493,7 @@ static const struct nla_policy set_settings_policy[ETHA_SETTINGS_MAX + 1] = {
 	[ETHA_SETTINGS_LINK_INFO]	= { .type = NLA_NESTED },
 	[ETHA_SETTINGS_LINK_MODES]	= { .type = NLA_NESTED },
 	[ETHA_SETTINGS_LINK_STATE]	= { .type = NLA_REJECT },
+	[ETHA_SETTINGS_WOL]		= { .type = NLA_REJECT },
 };
 
 static int ethnl_set_link_ksettings(struct genl_info *info,
-- 
2.21.0

Powered by blists - more mailing lists