From 86ccc2aea28f6f2dad8d03e452f9bd6d8daa9d5e Mon Sep 17 00:00:00 2001
From: Thierry Strudel <tstrudel@google.com>
Date: Wed, 11 May 2016 21:03:24 -0700
Subject: [PATCH] Revert "Revert "net: Fail explicit bind to local reserved
 ports""

This reverts commit 4253e58bf93e8c2169de190aaaf2f3355f806be7.
---
 Documentation/networking/ip-sysctl.txt | 5 +++++
 include/net/ip.h                       | 1 +
 net/ipv4/af_inet.c                     | 2 ++
 net/ipv4/inet_connection_sock.c        | 7 +++++++
 net/ipv4/sysctl_net_ipv4.c             | 7 +++++++
 net/ipv4/udp.c                         | 6 ++++++
 6 files changed, 28 insertions(+)

diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index e37265db036a..98de9e0fb856 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -698,6 +698,11 @@ ip_local_reserved_ports - list of comma separated ranges
 
 	Default: Empty
 
+reserved_port_bind - BOOLEAN
+	If set, allows explicit bind requests to applications requesting
+	any port within the range of ip_local_reserved_ports.
+	Default: 1
+
 ip_nonlocal_bind - BOOLEAN
 	If set, allows processes to bind() to non-local IP addresses,
 	which can be quite useful - but may break some applications.
diff --git a/include/net/ip.h b/include/net/ip.h
index 4afc28c1ae8a..e7dcf99dedd3 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -209,6 +209,7 @@ static inline int inet_is_reserved_local_port(int port)
 	return test_bit(port, sysctl_local_reserved_ports);
 }
 
+extern int sysctl_reserved_port_bind;
 extern int sysctl_ip_nonlocal_bind;
 
 /* From inetpeer.c */
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index b507a47acfb3..f5b558606c54 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -133,6 +133,8 @@ static inline int current_has_network(void)
 }
 #endif
 
+int sysctl_reserved_port_bind __read_mostly = 1;
+
 /* The inetsw table contains everything that inet_create needs to
  * build a new socket.
  */
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 6dfec2f18214..64198a381ddc 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -181,6 +181,13 @@ have_snum:
 		head = &hashinfo->bhash[inet_bhashfn(net, snum,
 				hashinfo->bhash_size)];
 		spin_lock(&head->lock);
+
+		if (inet_is_reserved_local_port(snum) &&
+		    !sysctl_reserved_port_bind) {
+			ret = 1;
+			goto fail_unlock;
+		}
+
 		inet_bind_bucket_for_each(tb, &head->chain)
 			if (net_eq(ib_net(tb), net) && tb->port == snum)
 				goto tb_found;
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 2b31c2d6f68d..aa54c1766bd0 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -506,6 +506,13 @@ static struct ctl_table ipv4_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_do_large_bitmap,
 	},
+	{
+		.procname	= "reserved_port_bind",
+		.data		= &sysctl_reserved_port_bind,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec
+	},
 	{
 		.procname	= "igmp_max_memberships",
 		.data		= &sysctl_igmp_max_memberships,
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 1c9d2685de3f..aac595f8d266 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -106,6 +106,7 @@
 #include <net/route.h>
 #include <net/checksum.h>
 #include <net/xfrm.h>
+#include <net/ip.h>
 #include <trace/events/udp.h>
 #include <linux/static_key.h>
 #include <trace/events/skb.h>
@@ -254,6 +255,11 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum,
 	} else {
 		hslot = udp_hashslot(udptable, net, snum);
 		spin_lock_bh(&hslot->lock);
+
+		if (inet_is_reserved_local_port(snum) &&
+		    !sysctl_reserved_port_bind)
+			goto fail_unlock;
+
 		if (hslot->count > 10) {
 			int exist;
 			unsigned int slot2 = udp_sk(sk)->udp_portaddr_hash ^ snum;
-- 
GitLab