Blame SOURCES/0126-Check-user-supplied-interface-name-lengths.patch

99be8f
From 358ca205cfc9646aefae6572607a0a1363086e51 Mon Sep 17 00:00:00 2001
99be8f
From: Andrea Claudi <aclaudi@redhat.com>
99be8f
Date: Mon, 29 Apr 2019 20:09:13 +0200
99be8f
Subject: [PATCH] Check user supplied interface name lengths
99be8f
99be8f
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1465646
99be8f
Upstream Status: iproute2.git commit 625df645b703d
99be8f
99be8f
commit 625df645b703dc858d54784c35beff64464afae2
99be8f
Author: Phil Sutter <phil@nwl.cc>
99be8f
Date:   Mon Oct 2 13:46:37 2017 +0200
99be8f
99be8f
    Check user supplied interface name lengths
99be8f
99be8f
    The original problem was that something like:
99be8f
99be8f
    | strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
99be8f
99be8f
    might leave ifr.ifr_name unterminated if length of *argv exceeds
99be8f
    IFNAMSIZ. In order to fix this, I thought about replacing all those
99be8f
    cases with (equivalent) calls to snprintf() or even introducing
99be8f
    strlcpy(). But as Ulrich Drepper correctly pointed out when rejecting
99be8f
    the latter from being added to glibc, truncating a string without
99be8f
    notifying the user is not to be considered good practice. So let's
99be8f
    excercise what he suggested and reject empty, overlong or otherwise
99be8f
    invalid interface names right from the start - this way calls to
99be8f
    strncpy() like shown above become safe and the user has a chance to
99be8f
    reconsider what he was trying to do.
99be8f
99be8f
    Note that this doesn't add calls to check_ifname() to all places where
99be8f
    user supplied interface name is parsed. In many cases, the interface
99be8f
    must exist already and is therefore looked up using ll_name_to_index(),
99be8f
    so if_nametoindex() will perform the necessary checks already.
99be8f
99be8f
    Signed-off-by: Phil Sutter <phil@nwl.cc>
99be8f
---
99be8f
 include/utils.h |  2 ++
99be8f
 ip/ip6tunnel.c  |  3 ++-
99be8f
 ip/ipl2tp.c     |  4 +++-
99be8f
 ip/iplink.c     | 31 ++++++++++++-------------------
99be8f
 ip/ipmaddr.c    |  3 ++-
99be8f
 ip/iprule.c     | 10 ++++++++--
99be8f
 ip/iptunnel.c   |  7 ++++++-
99be8f
 ip/iptuntap.c   |  6 ++++--
99be8f
 lib/utils.c     | 29 +++++++++++++++++++++++++++++
99be8f
 misc/arpd.c     |  3 ++-
99be8f
 tc/f_flower.c   |  2 ++
99be8f
 11 files changed, 72 insertions(+), 28 deletions(-)
99be8f
99be8f
diff --git a/include/utils.h b/include/utils.h
99be8f
index d596a6fc10574..0382460136180 100644
99be8f
--- a/include/utils.h
99be8f
+++ b/include/utils.h
99be8f
@@ -145,6 +145,8 @@ void missarg(const char *) __attribute__((noreturn));
99be8f
 void invarg(const char *, const char *) __attribute__((noreturn));
99be8f
 void duparg(const char *, const char *) __attribute__((noreturn));
99be8f
 void duparg2(const char *, const char *) __attribute__((noreturn));
99be8f
+int check_ifname(const char *);
99be8f
+int get_ifname(char *, const char *);
99be8f
 int matches(const char *arg, const char *pattern);
99be8f
 int inet_addr_match(const inet_prefix *a, const inet_prefix *b, int bits);
99be8f
 
99be8f
diff --git a/ip/ip6tunnel.c b/ip/ip6tunnel.c
99be8f
index c12d700e74189..bc44bef7f030c 100644
99be8f
--- a/ip/ip6tunnel.c
99be8f
+++ b/ip/ip6tunnel.c
99be8f
@@ -273,7 +273,8 @@ static int parse_args(int argc, char **argv, int cmd, struct ip6_tnl_parm2 *p)
99be8f
 				usage();
99be8f
 			if (p->name[0])
99be8f
 				duparg2("name", *argv);
99be8f
-			strncpy(p->name, *argv, IFNAMSIZ - 1);
99be8f
+			if (get_ifname(p->name, *argv))
99be8f
+				invarg("\"name\" not a valid ifname", *argv);
99be8f
 			if (cmd == SIOCCHGTUNNEL && count == 0) {
99be8f
 				struct ip6_tnl_parm2 old_p = {};
99be8f
 
99be8f
diff --git a/ip/ipl2tp.c b/ip/ipl2tp.c
99be8f
index 742adbe4f9c3a..7c5ed313b186f 100644
99be8f
--- a/ip/ipl2tp.c
99be8f
+++ b/ip/ipl2tp.c
99be8f
@@ -182,7 +182,7 @@ static int create_session(struct l2tp_parm *p)
99be8f
 	if (p->peer_cookie_len)
99be8f
 		addattr_l(&req.n, 1024, L2TP_ATTR_PEER_COOKIE,
99be8f
 			  p->peer_cookie,  p->peer_cookie_len);
99be8f
-	if (p->ifname && p->ifname[0])
99be8f
+	if (p->ifname)
99be8f
 		addattrstrz(&req.n, 1024, L2TP_ATTR_IFNAME, p->ifname);
99be8f
 
99be8f
 	if (rtnl_talk(&genl_rth, &req.n, NULL) < 0)
99be8f
@@ -545,6 +545,8 @@ static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
99be8f
 			}
99be8f
 		} else if (strcmp(*argv, "name") == 0) {
99be8f
 			NEXT_ARG();
99be8f
+			if (check_ifname(*argv))
99be8f
+				invarg("\"name\" not a valid ifname", *argv);
99be8f
 			p->ifname = *argv;
99be8f
 		} else if (strcmp(*argv, "remote") == 0) {
99be8f
 			NEXT_ARG();
99be8f
diff --git a/ip/iplink.c b/ip/iplink.c
99be8f
index db5b2c9645ba8..50f1075d94171 100644
99be8f
--- a/ip/iplink.c
99be8f
+++ b/ip/iplink.c
99be8f
@@ -581,6 +581,8 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
99be8f
 			req->i.ifi_flags &= ~IFF_UP;
99be8f
 		} else if (strcmp(*argv, "name") == 0) {
99be8f
 			NEXT_ARG();
99be8f
+			if (check_ifname(*argv))
99be8f
+				invarg("\"name\" not a valid ifname", *argv);
99be8f
 			*name = *argv;
99be8f
 		} else if (strcmp(*argv, "index") == 0) {
99be8f
 			NEXT_ARG();
99be8f
@@ -848,6 +850,8 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
99be8f
 				NEXT_ARG();
99be8f
 			if (*dev)
99be8f
 				duparg2("dev", *argv);
99be8f
+			if (check_ifname(*argv))
99be8f
+				invarg("\"dev\" not a valid ifname", *argv);
99be8f
 			*dev = *argv;
99be8f
 			dev_index = ll_name_to_index(*dev);
99be8f
 		}
99be8f
@@ -870,7 +874,6 @@ int iplink_parse(int argc, char **argv, struct iplink_req *req,
99be8f
 
99be8f
 static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
99be8f
 {
99be8f
-	int len;
99be8f
 	char *dev = NULL;
99be8f
 	char *name = NULL;
99be8f
 	char *link = NULL;
99be8f
@@ -960,13 +963,8 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
99be8f
 	}
99be8f
 
99be8f
 	if (name) {
99be8f
-		len = strlen(name) + 1;
99be8f
-		if (len == 1)
99be8f
-			invarg("\"\" is not a valid device identifier\n",
99be8f
-			       "name");
99be8f
-		if (len > IFNAMSIZ)
99be8f
-			invarg("\"name\" too long\n", name);
99be8f
-		addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len);
99be8f
+		addattr_l(&req.n, sizeof(req),
99be8f
+			  IFLA_IFNAME, name, strlen(name) + 1);
99be8f
 	}
99be8f
 
99be8f
 	if (type) {
99be8f
@@ -1016,7 +1014,6 @@ static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
99be8f
 
99be8f
 int iplink_get(unsigned int flags, char *name, __u32 filt_mask)
99be8f
 {
99be8f
-	int len;
99be8f
 	struct iplink_req req = {
99be8f
 		.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
99be8f
 		.n.nlmsg_flags = NLM_F_REQUEST | flags,
99be8f
@@ -1026,13 +1023,8 @@ int iplink_get(unsigned int flags, char *name, __u32 filt_mask)
99be8f
 	struct nlmsghdr *answer;
99be8f
 
99be8f
 	if (name) {
99be8f
-		len = strlen(name) + 1;
99be8f
-		if (len == 1)
99be8f
-			invarg("\"\" is not a valid device identifier\n",
99be8f
-				   "name");
99be8f
-		if (len > IFNAMSIZ)
99be8f
-			invarg("\"name\" too long\n", name);
99be8f
-		addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len);
99be8f
+		addattr_l(&req.n, sizeof(req),
99be8f
+			  IFLA_IFNAME, name, strlen(name) + 1);
99be8f
 	}
99be8f
 	addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask);
99be8f
 
99be8f
@@ -1256,6 +1248,8 @@ static int do_set(int argc, char **argv)
99be8f
 			flags &= ~IFF_UP;
99be8f
 		} else if (strcmp(*argv, "name") == 0) {
99be8f
 			NEXT_ARG();
99be8f
+			if (check_ifname(*argv))
99be8f
+				invarg("\"name\" not a valid ifname", *argv);
99be8f
 			newname = *argv;
99be8f
 		} else if (matches(*argv, "address") == 0) {
99be8f
 			NEXT_ARG();
99be8f
@@ -1346,6 +1340,8 @@ static int do_set(int argc, char **argv)
99be8f
 
99be8f
 			if (dev)
99be8f
 				duparg2("dev", *argv);
99be8f
+			if (check_ifname(*argv))
99be8f
+				invarg("\"dev\" not a valid ifname", *argv);
99be8f
 			dev = *argv;
99be8f
 		}
99be8f
 		argc--; argv++;
99be8f
@@ -1374,9 +1370,6 @@ static int do_set(int argc, char **argv)
99be8f
 	}
99be8f
 
99be8f
 	if (newname && strcmp(dev, newname)) {
99be8f
-		if (strlen(newname) == 0)
99be8f
-			invarg("\"\" is not a valid device identifier\n",
99be8f
-			       "name");
99be8f
 		if (do_changename(dev, newname) < 0)
99be8f
 			return -1;
99be8f
 		dev = newname;
99be8f
diff --git a/ip/ipmaddr.c b/ip/ipmaddr.c
99be8f
index 85a69e779563d..5683f6fa830c1 100644
99be8f
--- a/ip/ipmaddr.c
99be8f
+++ b/ip/ipmaddr.c
99be8f
@@ -284,7 +284,8 @@ static int multiaddr_modify(int cmd, int argc, char **argv)
99be8f
 			NEXT_ARG();
99be8f
 			if (ifr.ifr_name[0])
99be8f
 				duparg("dev", *argv);
99be8f
-			strncpy(ifr.ifr_name, *argv, IFNAMSIZ);
99be8f
+			if (get_ifname(ifr.ifr_name, *argv))
99be8f
+				invarg("\"dev\" not a valid ifname", *argv);
99be8f
 		} else {
99be8f
 			if (matches(*argv, "address") == 0) {
99be8f
 				NEXT_ARG();
99be8f
diff --git a/ip/iprule.c b/ip/iprule.c
99be8f
index e64b4d7db2815..201d3bdc20427 100644
99be8f
--- a/ip/iprule.c
99be8f
+++ b/ip/iprule.c
99be8f
@@ -472,11 +472,13 @@ static int iprule_list_flush_or_save(int argc, char **argv, int action)
99be8f
 		} else if (strcmp(*argv, "dev") == 0 ||
99be8f
 			   strcmp(*argv, "iif") == 0) {
99be8f
 			NEXT_ARG();
99be8f
-			strncpy(filter.iif, *argv, IFNAMSIZ);
99be8f
+			if (get_ifname(filter.iif, *argv))
99be8f
+				invarg("\"iif\"/\"dev\" not a valid ifname", *argv);
99be8f
 			filter.iifmask = 1;
99be8f
 		} else if (strcmp(*argv, "oif") == 0) {
99be8f
 			NEXT_ARG();
99be8f
-			strncpy(filter.oif, *argv, IFNAMSIZ);
99be8f
+			if (get_ifname(filter.oif, *argv))
99be8f
+				invarg("\"oif\" not a valid ifname", *argv);
99be8f
 			filter.oifmask = 1;
99be8f
 		} else if (strcmp(*argv, "l3mdev") == 0) {
99be8f
 			filter.l3mdev = 1;
99be8f
@@ -695,10 +697,14 @@ static int iprule_modify(int cmd, int argc, char **argv)
99be8f
 		} else if (strcmp(*argv, "dev") == 0 ||
99be8f
 			   strcmp(*argv, "iif") == 0) {
99be8f
 			NEXT_ARG();
99be8f
+			if (check_ifname(*argv))
99be8f
+				invarg("\"iif\"/\"dev\" not a valid ifname", *argv);
99be8f
 			addattr_l(&req.n, sizeof(req), FRA_IFNAME,
99be8f
 				  *argv, strlen(*argv)+1);
99be8f
 		} else if (strcmp(*argv, "oif") == 0) {
99be8f
 			NEXT_ARG();
99be8f
+			if (check_ifname(*argv))
99be8f
+				invarg("\"oif\" not a valid ifname", *argv);
99be8f
 			addattr_l(&req.n, sizeof(req), FRA_OIFNAME,
99be8f
 				  *argv, strlen(*argv)+1);
99be8f
 		} else if (strcmp(*argv, "l3mdev") == 0) {
99be8f
diff --git a/ip/iptunnel.c b/ip/iptunnel.c
99be8f
index 0acfd0793d3cd..208a1f06ab12f 100644
99be8f
--- a/ip/iptunnel.c
99be8f
+++ b/ip/iptunnel.c
99be8f
@@ -178,7 +178,8 @@ static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
99be8f
 
99be8f
 			if (p->name[0])
99be8f
 				duparg2("name", *argv);
99be8f
-			strncpy(p->name, *argv, IFNAMSIZ - 1);
99be8f
+			if (get_ifname(p->name, *argv))
99be8f
+				invarg("\"name\" not a valid ifname", *argv);
99be8f
 			if (cmd == SIOCCHGTUNNEL && count == 0) {
99be8f
 				struct ip_tunnel_parm old_p = {};
99be8f
 
99be8f
@@ -487,6 +488,8 @@ static int do_prl(int argc, char **argv)
99be8f
 			count++;
99be8f
 		} else if (strcmp(*argv, "dev") == 0) {
99be8f
 			NEXT_ARG();
99be8f
+			if (check_ifname(*argv))
99be8f
+				invarg("\"dev\" not a valid ifname", *argv);
99be8f
 			medium = *argv;
99be8f
 		} else {
99be8f
 			fprintf(stderr,
99be8f
@@ -534,6 +537,8 @@ static int do_6rd(int argc, char **argv)
99be8f
 			cmd = SIOCDEL6RD;
99be8f
 		} else if (strcmp(*argv, "dev") == 0) {
99be8f
 			NEXT_ARG();
99be8f
+			if (check_ifname(*argv))
99be8f
+				invarg("\"dev\" not a valid ifname", *argv);
99be8f
 			medium = *argv;
99be8f
 		} else {
99be8f
 			fprintf(stderr,
99be8f
diff --git a/ip/iptuntap.c b/ip/iptuntap.c
99be8f
index 451f7f0eac6bb..b46e452f21278 100644
99be8f
--- a/ip/iptuntap.c
99be8f
+++ b/ip/iptuntap.c
99be8f
@@ -176,7 +176,8 @@ static int parse_args(int argc, char **argv,
99be8f
 			ifr->ifr_flags |= IFF_MULTI_QUEUE;
99be8f
 		} else if (matches(*argv, "dev") == 0) {
99be8f
 			NEXT_ARG();
99be8f
-			strncpy(ifr->ifr_name, *argv, IFNAMSIZ-1);
99be8f
+			if (get_ifname(ifr->ifr_name, *argv))
99be8f
+				invarg("\"dev\" not a valid ifname", *argv);
99be8f
 		} else {
99be8f
 			if (matches(*argv, "name") == 0) {
99be8f
 				NEXT_ARG();
99be8f
@@ -184,7 +185,8 @@ static int parse_args(int argc, char **argv,
99be8f
 				usage();
99be8f
 			if (ifr->ifr_name[0])
99be8f
 				duparg2("name", *argv);
99be8f
-			strncpy(ifr->ifr_name, *argv, IFNAMSIZ);
99be8f
+			if (get_ifname(ifr->ifr_name, *argv))
99be8f
+				invarg("\"name\" not a valid ifname", *argv);
99be8f
 		}
99be8f
 		count++;
99be8f
 		argc--; argv++;
99be8f
diff --git a/lib/utils.c b/lib/utils.c
99be8f
index 228d97bfe5e9b..0c56f0b478f23 100644
99be8f
--- a/lib/utils.c
99be8f
+++ b/lib/utils.c
99be8f
@@ -20,6 +20,7 @@
99be8f
 #include <sys/socket.h>
99be8f
 #include <netinet/in.h>
99be8f
 #include <string.h>
99be8f
+#include <ctype.h>
99be8f
 #include <netdb.h>
99be8f
 #include <arpa/inet.h>
99be8f
 #include <asm/types.h>
99be8f
@@ -697,6 +698,34 @@ void duparg2(const char *key, const char *arg)
99be8f
 	exit(-1);
99be8f
 }
99be8f
 
99be8f
+int check_ifname(const char *name)
99be8f
+{
99be8f
+	/* These checks mimic kernel checks in dev_valid_name */
99be8f
+	if (*name == '\0')
99be8f
+		return -1;
99be8f
+	if (strlen(name) >= IFNAMSIZ)
99be8f
+		return -1;
99be8f
+
99be8f
+	while (*name) {
99be8f
+		if (*name == '/' || isspace(*name))
99be8f
+			return -1;
99be8f
+		++name;
99be8f
+	}
99be8f
+	return 0;
99be8f
+}
99be8f
+
99be8f
+/* buf is assumed to be IFNAMSIZ */
99be8f
+int get_ifname(char *buf, const char *name)
99be8f
+{
99be8f
+	int ret;
99be8f
+
99be8f
+	ret = check_ifname(name);
99be8f
+	if (ret == 0)
99be8f
+		strncpy(buf, name, IFNAMSIZ);
99be8f
+
99be8f
+	return ret;
99be8f
+}
99be8f
+
99be8f
 int matches(const char *cmd, const char *pattern)
99be8f
 {
99be8f
 	int len = strlen(cmd);
99be8f
diff --git a/misc/arpd.c b/misc/arpd.c
99be8f
index c9d86475e5995..67d86b67957b8 100644
99be8f
--- a/misc/arpd.c
99be8f
+++ b/misc/arpd.c
99be8f
@@ -662,7 +662,8 @@ int main(int argc, char **argv)
99be8f
 		struct ifreq ifr = {};
99be8f
 
99be8f
 		for (i = 0; i < ifnum; i++) {
99be8f
-			strncpy(ifr.ifr_name, ifnames[i], IFNAMSIZ);
99be8f
+			if (get_ifname(ifr.ifr_name, ifnames[i]))
99be8f
+				invarg("not a valid ifname", ifnames[i]);
99be8f
 			if (ioctl(udp_sock, SIOCGIFINDEX, &ifr)) {
99be8f
 				perror("ioctl(SIOCGIFINDEX)");
99be8f
 				exit(-1);
99be8f
diff --git a/tc/f_flower.c b/tc/f_flower.c
99be8f
index 34249254603ff..f3f8d3427c761 100644
99be8f
--- a/tc/f_flower.c
99be8f
+++ b/tc/f_flower.c
99be8f
@@ -643,6 +643,8 @@ static int flower_parse_opt(struct filter_util *qu, char *handle,
99be8f
 			flags |= TCA_CLS_FLAGS_SKIP_SW;
99be8f
 		} else if (matches(*argv, "indev") == 0) {
99be8f
 			NEXT_ARG();
99be8f
+			if (check_ifname(*argv))
99be8f
+				invarg("\"indev\" not a valid ifname", *argv);
99be8f
 			addattrstrz(n, MAX_MSG, TCA_FLOWER_INDEV, *argv);
99be8f
 		} else if (matches(*argv, "vlan_id") == 0) {
99be8f
 			__u16 vid;
99be8f
-- 
99be8f
2.20.1
99be8f