diff -Nurd linux-orig/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-orig/Documentation/Configure.help	2003-08-25 04:44:39.000000000 -0700
+++ linux/Documentation/Configure.help	2003-08-29 06:57:45.000000000 -0700
@@ -3252,6 +3252,73 @@
 
   If unsure, say N.
 
+IP: port/socket pseudo ACLs
+CONFIG_PSEUDO_ACLS
+  If you say yes here, config will ask a few more questions about port
+  and socket ACLs.
+
+IP: port pseudo ACLs
+CONFIG_PORT_ACLS
+  This simple hack allows root to delegate the ability to bind to a
+  reserved port (tcp/udp port < 1024) to non-privileged users, much
+  like CAP_NET_BIND_SERVICE; however, it allows much finer access
+  control than the capability.  By setgid-ing any binary owned by the
+  gid specified in /proc/sys/net/ipv[46]/ip_port_acl_gid, non-root users
+  will be able to use that binary to bind to the tcp or udp port equal
+  to their system uid.
+
+  For instance, perhaps one would like to run snmpd as the non-privileged
+  user "snmp," simply:
+
+    Say yes here and compile a new kernel, then reboot into it.
+    Create a user "snmp" in /etc/passwd with a uid of 161.
+    Create a group "portacl" with an unused gid, $gid here.
+    # echo $gid > /proc/sys/net/ipv4/ip_port_acl_gid
+    # chown root.$gid /usr/sbin/snmpd
+    # chmod 2755 /usr/sbin/snmpd
+    # su - snmp -c /usr/sbin/snmpd -r
+
+   You will most likely need to tweak the snmpd configs a bit, too.
+
+   For more info, check http://killa.net/infosec/acls/index.htm
+
+IP: Raw socket group
+CONFIG_SOCK_RAW_GROUP
+  Say yes here if you wish to give a special group access to raw sockets.
+  Any raw-socket-needing binary setgid this group will not require root
+  access to do its duty.  This may mitigate any as yet unknown exploits
+  for a class of setuid zero networking applications, such as ping,
+  traceroute or mtr.
+
+  SOCK_RAW sockets work at the IP level, layer 3 (Network) in the
+  OSI model.
+
+  You will need to setgid any binaries needing raw sockets to a new gid,
+  and specify that gid in /proc/sys/net/ipv[46]/ip_sock_raw_gid for it
+  to take effect.
+
+  For more info, check http://killa.net/infosec/acls/index.html
+
+IP: Packet socket group
+CONFIG_SOCK_PACKET_GROUP
+  This option allows you to split off packet socket privileges to a
+  seperate group.  Unlike CAP_NET_RAW, you will have finer control over
+  which binaries will get SOCK_PACKET privs and which get SOCK_RAW privs.
+
+  SOCK_PACKET sockets work at the device driver level, layer 2 (data link)
+  in the OSI model.  As such, it is a lower level interface than SOCK_RAW.
+  The most common binaries that require packet socket access are those
+  which use the functionality of libpcap (e.g. tcpdump).
+
+  You will need to setgid any binaries needing packet sockets to a new
+  gid, and specify that gid in /proc/sys/net/core/sock_packet_gid for
+  it to take effect.
+
+  NOTE: I recommend you say yes, unless you have a (weird) application
+  that needs both raw and packet sockets.
+
+  For more info, check http://killa.net/infosec/acls/index.html
+
 # Choice: alphatype
 Alpha system type
 CONFIG_ALPHA_GENERIC
diff -Nurd linux-orig/include/linux/sysctl.h linux/include/linux/sysctl.h
--- linux-orig/include/linux/sysctl.h	2003-08-25 04:44:44.000000000 -0700
+++ linux/include/linux/sysctl.h	2003-08-29 06:57:45.000000000 -0700
@@ -209,7 +209,8 @@
 	NET_CORE_NO_CONG=14,
 	NET_CORE_LO_CONG=15,
 	NET_CORE_MOD_CONG=16,
-	NET_CORE_DEV_WEIGHT=17
+	NET_CORE_DEV_WEIGHT=17,
+	NET_CORE_SOCK_PACKET_GID=18
 };
 
 /* /proc/sys/net/ethernet */
@@ -299,6 +300,8 @@
 	NET_TCP_FRTO=92,
 	NET_TCP_LOW_LATENCY=93,
 	NET_IPV4_IPFRAG_SECRET_INTERVAL=94,
+	NET_IPV4_PORT_ACL_GID=95,
+	NET_IPV4_SOCK_RAW_GID=96
 };
 
 enum {
@@ -379,7 +382,9 @@
 	NET_IPV6_DAD_TRANSMITS=7,
 	NET_IPV6_RTR_SOLICITS=8,
 	NET_IPV6_RTR_SOLICIT_INTERVAL=9,
-	NET_IPV6_RTR_SOLICIT_DELAY=10
+	NET_IPV6_RTR_SOLICIT_DELAY=10,
+	NET_IPV6_PORT_ACL_GID=11,
+	NET_IPV6_SOCK_RAW_GID=12
 };
 
 /* /proc/sys/net/ipv6/icmp */
diff -Nurd linux-orig/net/Config.in linux/net/Config.in
--- linux-orig/net/Config.in	2003-08-25 04:44:44.000000000 -0700
+++ linux/net/Config.in	2003-08-29 06:57:45.000000000 -0700
@@ -15,6 +15,26 @@
    bool '  Network packet filtering debugging' CONFIG_NETFILTER_DEBUG
 fi
 bool 'Socket Filtering'  CONFIG_FILTER
+
+#mainmenu_option next_comment
+#comment 'Port/Socket Pseudo ACLs'
+if [ "$CONFIG_SYSCTL" = "y" ]
+then
+	bool 'Port/Socket Pseudo ACLs' CONFIG_PSEUDO_ACLS
+	if [ "$CONFIG_PSEUDO_ACLS" = "y" ]; then
+		bool '  Port ACLs' CONFIG_PORT_ACLS
+		bool '  Raw socket group' CONFIG_SOCK_RAW_GROUP
+		if [ "$CONFIG_PACKET" != "n" -a "$CONFIG_SOCK_RAW_GROUP" = "y" ]; then
+			if [ "$CONFIG_PACKET" = "y" ]; then
+				bool '    Packet socket group' CONFIG_SOCK_PACKET_GROUP
+			else
+				comment '    Packet socket group not available in a module'
+			fi
+		fi
+	fi
+fi
+#endmenu
+
 tristate 'Unix domain sockets' CONFIG_UNIX
 bool 'TCP/IP networking' CONFIG_INET
 if [ "$CONFIG_INET" = "y" ]; then
@@ -99,4 +119,5 @@
 tristate 'Packet Generator (USE WITH CAUTION)' CONFIG_NET_PKTGEN
 endmenu
 
+
 endmenu
diff -Nurd linux-orig/net/core/sysctl_net_core.c linux/net/core/sysctl_net_core.c
--- linux-orig/net/core/sysctl_net_core.c	2002-11-28 15:53:15.000000000 -0800
+++ linux/net/core/sysctl_net_core.c	2003-08-29 06:57:45.000000000 -0700
@@ -34,6 +34,10 @@
 extern char sysctl_divert_version[];
 #endif /* CONFIG_NET_DIVERT */
 
+#ifdef CONFIG_SOCK_PACKET_GROUP
+extern int sysctl_sock_packet_gid;
+#endif /* CONFIG_SOCK_PACKET_GROUP */
+
 ctl_table core_table[] = {
 #ifdef CONFIG_NET
 	{NET_CORE_WMEM_MAX, "wmem_max",
@@ -88,6 +92,11 @@
 	 (void *)sysctl_divert_version, 32, 0444, NULL,
 	 &proc_dostring},
 #endif /* CONFIG_NET_DIVERT */
+#ifdef CONFIG_SOCK_PACKET_GROUP
+         {NET_CORE_SOCK_PACKET_GID, "sock_packet_gid",
+	  &sysctl_sock_packet_gid, sizeof(int), 0644, NULL,
+	  &proc_dointvec},
+#endif /* CONFIG_SOCK_PACKET_GROUP */
 #endif /* CONFIG_NET */
 	{ 0 }
 };
diff -Nurd linux-orig/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- linux-orig/net/ipv4/af_inet.c	2003-06-13 07:51:39.000000000 -0700
+++ linux/net/ipv4/af_inet.c	2003-08-29 06:57:45.000000000 -0700
@@ -147,6 +147,14 @@
 int (*vlan_ioctl_hook)(unsigned long arg);
 #endif
 
+#ifdef CONFIG_PORT_ACLS
+int sysctl_ipv4_port_acl_gid;
+#endif /* CONFIG_PORT_ACLS */
+
+#ifdef CONFIG_SOCK_RAW_GROUP
+int sysctl_ipv4_sock_raw_gid;
+#endif /* CONFIG_SOCK_RAW_GROUP */
+
 /* The inetsw table contains everything that inet_create needs to
  * build a new socket.
  */
@@ -352,7 +360,12 @@
 
 	if (!answer)
 		goto free_and_badtype;
-	if (answer->capability > 0 && !capable(answer->capability))
+	if (answer->capability > 0 && !capable(answer->capability)
+#ifdef CONFIG_SOCK_RAW_GROUP
+		&& ((answer->type == SOCK_RAW) &&
+			(current->egid != sysctl_ipv4_sock_raw_gid))
+#endif /*CONFIG_SOCK_RAW_GROUP */
+	   )
 		goto free_and_badperm;
 	if (!protocol)
 		goto free_and_noproto;
@@ -503,7 +516,12 @@
 		return -EADDRNOTAVAIL;
 
 	snum = ntohs(addr->sin_port);
-	if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+	if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)
+#ifdef CONFIG_PORT_ACLS
+		&& !((current->uid == snum) &&
+			(current->egid == sysctl_ipv4_port_acl_gid))
+#endif /* CONFIG_PORT_ACLS */
+	   )
 		return -EACCES;
 
 	/*      We keep a pair of addresses. rcv_saddr is the one
diff -Nurd linux-orig/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c
--- linux-orig/net/ipv4/sysctl_net_ipv4.c	2003-06-13 07:51:39.000000000 -0700
+++ linux/net/ipv4/sysctl_net_ipv4.c	2003-08-29 06:57:45.000000000 -0700
@@ -56,6 +56,14 @@
 
 extern ctl_table ipv4_route_table[];
 
+#ifdef CONFIG_PORT_ACLS
+extern int sysctl_ipv4_port_acl_gid;
+#endif /* CONFIG_PORT_ACLS */
+
+#ifdef CONFIG_SOCK_RAW_GROUP
+extern int sysctl_ipv4_sock_raw_gid;
+#endif /* CONFIG_SOCK_RAW_GROUP */
+
 #ifdef CONFIG_SYSCTL
 
 static
@@ -229,6 +237,14 @@
 	{NET_IPV4_IPFRAG_SECRET_INTERVAL, "ipfrag_secret_interval",
 	 &sysctl_ipfrag_secret_interval, sizeof(int), 0644, NULL, &proc_dointvec_jiffies, 
 	 &sysctl_jiffies},
+#ifdef CONFIG_PORT_ACLS
+	{NET_IPV4_PORT_ACL_GID, "ip_port_acl_gid",
+	 &sysctl_ipv4_port_acl_gid, sizeof(int), 0644, NULL, &proc_dointvec},
+#endif /*CONFIG_PORT_ACLS */
+#ifdef CONFIG_SOCK_RAW_GROUP
+	{NET_IPV4_SOCK_RAW_GID, "ip_sock_raw_gid",
+	 &sysctl_ipv4_sock_raw_gid, sizeof(int), 0644, NULL, &proc_dointvec},
+#endif /* CONFIG_SOCK_RAW_GROUP */
 	{0}
 };
 
diff -Nurd linux-orig/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- linux-orig/net/ipv6/af_inet6.c	2003-08-25 04:44:44.000000000 -0700
+++ linux/net/ipv6/af_inet6.c	2003-08-29 06:57:45.000000000 -0700
@@ -73,6 +73,14 @@
 MODULE_PARM(unloadable, "i");
 #endif
 
+#ifdef CONFIG_PORT_ACLS
+int sysctl_ipv6_port_acl_gid;
+#endif /* CONFIG_PORT_ACLS */
+
+#ifdef CONFIG_SOCK_RAW_GROUP
+int sysctl_ipv6_sock_raw_gid;
+#endif /* CONFIG_SOCK_RAW_GROUP */
+
 /* IPv6 procfs goodies... */
 
 #ifdef CONFIG_PROC_FS
@@ -145,7 +153,12 @@
 
 	if (!answer)
 		goto free_and_badtype;
-	if (answer->capability > 0 && !capable(answer->capability))
+	if (answer->capability > 0 && !capable(answer->capability)
+#ifdef CONFIG_SOCK_RAW_GROUP
+		&& ((answer->type == SOCK_RAW) &&
+				(current->egid != sysctl_ipv6_sock_raw_gid))
+#endif /* CONFIG_SOCK_RAW_GROUP */
+	   )
 		goto free_and_badperm;
 	if (!protocol)
 		goto free_and_noproto;
@@ -270,7 +283,12 @@
 	}
 
 	snum = ntohs(addr->sin6_port);
-	if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+	if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)
+#ifdef CONFIG_PORT_ACLS
+		&& !((current->uid == snum) &&
+			(current->egid == sysctl_ipv6_port_acl_gid))
+#endif /* CONFIG_PORT_ACLS */
+	   )
 		return -EACCES;
 
 	lock_sock(sk);
diff -Nurd linux-orig/net/ipv6/sysctl_net_ipv6.c linux/net/ipv6/sysctl_net_ipv6.c
--- linux-orig/net/ipv6/sysctl_net_ipv6.c	2003-06-13 07:51:39.000000000 -0700
+++ linux/net/ipv6/sysctl_net_ipv6.c	2003-08-29 06:57:45.000000000 -0700
@@ -17,6 +17,14 @@
 extern ctl_table ipv6_route_table[];
 extern ctl_table ipv6_icmp_table[];
 
+#ifdef CONFIG_PORT_ACLS
+extern int sysctl_ipv6_port_acl_gid;
+#endif /* CONFIG_PORT_ACLS */
+
+#ifdef CONFIG_SOCK_RAW_GROUP
+extern int sysctl_ipv6_sock_raw_gid;
+#endif /* CONFIG_SOCK_RAW_GROUP */
+
 #ifdef CONFIG_SYSCTL
 
 ctl_table ipv6_table[] = {
@@ -24,6 +32,14 @@
 	{NET_IPV6_ICMP, "icmp", NULL, 0, 0500, ipv6_icmp_table},
 	{NET_IPV6_BINDV6ONLY, "bindv6only",
 	 &sysctl_ipv6_bindv6only, sizeof(int), 0644, NULL, &proc_dointvec},
+#ifdef CONFIG_PORT_ACLS
+	{NET_IPV6_PORT_ACL_GID, "ip_port_acl_gid",
+	 &sysctl_ipv6_port_acl_gid, sizeof(int), 0644, NULL, &proc_dointvec},
+#endif /* CONFIG_PORT_ACLS */
+#ifdef CONFIG_SOCK_RAW_GROUP
+	{NET_IPV6_SOCK_RAW_GID, "ip_sock_raw_gid",
+	 &sysctl_ipv6_sock_raw_gid, sizeof(int), 0644, NULL, &proc_dointvec},
+#endif /* CONFIG_SOCK_RAW_GROUP */
 	{0}
 };
 
diff -Nurd linux-orig/net/packet/af_packet.c linux/net/packet/af_packet.c
--- linux-orig/net/packet/af_packet.c	2003-08-25 04:44:44.000000000 -0700
+++ linux/net/packet/af_packet.c	2003-08-29 06:57:45.000000000 -0700
@@ -83,6 +83,14 @@
 
 #define CONFIG_SOCK_PACKET	1
 
+#ifdef CONFIG_SOCK_RAW_GROUP
+#ifdef CONFIG_SOCK_PACKET_GROUP
+int sysctl_sock_packet_gid;
+#else
+extern int sysctl_sock_raw_gid;
+#endif /* CONFIG_SOCK_PACKET_GROUP */
+#endif /* CONFIG_SOCK_RAW_GROUP */
+
 /*
    Proposed replacement for SIOC{ADD,DEL}MULTI and
    IFF_PROMISC, IFF_ALLMULTI flags.
@@ -940,7 +948,15 @@
 	struct sock *sk;
 	int err;
 
-	if (!capable(CAP_NET_RAW))
+	if (!capable(CAP_NET_RAW)
+#ifdef CONFIG_SOCK_RAW_GROUP
+#ifdef CONFIG_SOCK_PACKET_GROUP
+		&& (current->egid != sysctl_sock_packet_gid)
+#else
+		&& (current->egid != sysctl_sock_raw_gid)
+#endif /* CONFIG_SOCK_PACKET_GROUP */
+#endif /* CONFIG_SOCK_RAW_GROUP */
+	   )
 		return -EPERM;
 	if (sock->type != SOCK_DGRAM && sock->type != SOCK_RAW
 #ifdef CONFIG_SOCK_PACKET
