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

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