Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 2 May 1999 00:17:28 -0400 (EDT)
From:      Brian Feldman <green@unixhelp.org>
To:        freebsd-gnats-submit@freebsd.org
Subject:   kern/11435: IPFW uid/gid support
Message-ID:  <Pine.BSF.4.10.9905020016120.66712-100000@janus.syracuse.net>

next in thread | raw e-mail | index | archive | help

>Number:         11435
>Category:       kern
>Synopsis:       IPFW had no per-uid or per-gid rule support.
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sat May  1 21:20:01 PDT 1999
>Closed-Date:
>Last-Modified:
>Originator:     Brian Feldman
>Release:        FreeBSD 4.0-CURRENT i386
>Organization:
>Environment:

	I use 4.0-CURRENT. The code should backport easily to 3.X.

>Description:

	I've added IPFW uid and gid rule options. This will work for filtering
packets for both TCP and UDP. Since these are the two IP user-controlled
protocols, and most useful anyway. The only other common Internet protocol
would be ICMP, which is not a user protocol anyway.

>How-To-Repeat:

	This doesn't apply, does it?

>Fix:
	
--- src/sbin/ipfw/ipfw.c.orig	Thu Jan 21 20:46:32 1999
+++ src/sbin/ipfw/ipfw.c	Wed Apr 21 07:41:03 1999
@@ -30,8 +30,10 @@
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
+#include <grp.h>
 #include <limits.h>
 #include <netdb.h>
+#include <pwd.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -333,6 +335,24 @@
 		}
 	}
 
+	if (chain->fw_flg & IP_FW_F_UID) {
+		struct passwd *pwd = getpwuid(chain->fw_uid);
+
+		if (pwd)
+			printf(" uid %s", pwd->pw_name);
+		else
+			printf(" uid %u", chain->fw_uid);
+	}
+
+	if (chain->fw_flg & IP_FW_F_GID) {
+		struct group *grp = getgrgid(chain->fw_gid);
+
+		if (grp)
+			printf(" gid %s", grp->gr_name);
+		else
+			printf(" gid %u", chain->fw_gid);
+	}
+
 	/* Direction */
 	if ((chain->fw_flg & IP_FW_F_IN) && !(chain->fw_flg & IP_FW_F_OUT))
 		printf(" in");
@@ -586,6 +606,8 @@
 "    src: from [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
 "    dst: to [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
 "  extras:\n"
+"    uid {user id}\n"
+"    gid {group id}\n"
 "    fragment     (may not be used with ports or tcpflags)\n"
 "    in\n"
 "    out\n"
@@ -1185,6 +1207,32 @@
 	}
 
 	while (ac) {
+		if (!strncmp(*av,"uid",strlen(*av))) {
+			struct passwd *pwd;
+
+			rule.fw_flg |= IP_FW_F_UID;
+			ac--; av++;
+			if (!ac)
+				show_usage("``uid'' requires argument");
+			
+			rule.fw_uid = (pwd = getpwnam(*av)) ? pwd->pw_uid
+					: strtoul(*av, NULL, 0);
+			ac--; av++;
+			continue;
+		}
+		if (!strncmp(*av,"gid",strlen(*av))) {
+			struct group *grp;
+
+			rule.fw_flg |= IP_FW_F_GID;
+			ac--; av++;
+			if (!ac)
+				show_usage("``gid'' requires argument");
+			
+			rule.fw_gid = (grp = getgrnam(*av)) ? (gid_t)grp->gr_gid
+					: strtoul(*av, NULL, 0);
+			ac--; av++;
+			continue;
+		}
 		if (!strncmp(*av,"in",strlen(*av))) { 
 			rule.fw_flg |= IP_FW_F_IN;
 			av++; ac--; continue;
--- src/sys/sys/socketvar.h.orig	Tue Apr 20 23:27:58 1999
+++ src/sys/sys/socketvar.h	Tue Apr 20 23:27:15 1999
@@ -109,6 +109,7 @@
 	/* NB: generation count must not be first; easiest to make it last. */
 	so_gen_t so_gencnt;		/* generation count */
 	void	*so_emuldata;		/* private data for emulators */
+	struct pcred *so_cred;		/* user credentials */
 };
 
 /*
--- src/sys/kern/uipc_socket.c.orig	Tue Apr 20 23:30:54 1999
+++ src/sys/kern/uipc_socket.c	Wed Apr 21 00:52:03 1999
@@ -36,13 +36,14 @@
 
 #include <sys/param.h>
 #include <sys/systm.h>
-#include <sys/proc.h>
 #include <sys/fcntl.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
 #include <sys/domain.h>
 #include <sys/kernel.h>
+#include <sys/malloc.h>
 #include <sys/poll.h>
+#include <sys/proc.h>
 #include <sys/protosw.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
@@ -124,8 +125,11 @@
 	TAILQ_INIT(&so->so_incomp);
 	TAILQ_INIT(&so->so_comp);
 	so->so_type = type;
-	if (p != 0)
+	if (p != 0) {
 		so->so_uid = p->p_ucred->cr_uid;
+		so->so_cred = p->p_cred;
+		p->p_cred->p_refcnt++;
+	} else so->so_cred = NULL;
 	so->so_proto = prp;
 	error = (*prp->pr_usrreqs->pru_attach)(so, proto, p);
 	if (error) {
@@ -156,6 +160,10 @@
 	struct socket *so;
 {
 	so->so_gencnt = ++so_gencnt;
+	if (so->so_cred && --so->so_cred->p_refcnt == 0) {
+		crfree(so->so_cred->pc_ucred);
+		FREE(so->so_cred, M_SUBPROC);
+	}
 	zfreei(so->so_zone, so);
 }
 
--- src/sys/kern/uipc_socket2.c.orig	Tue Apr 20 23:31:08 1999
+++ src/sys/kern/uipc_socket2.c	Wed Apr 21 00:46:18 1999
@@ -214,6 +214,10 @@
 	so->so_proto = head->so_proto;
 	so->so_timeo = head->so_timeo;
 	so->so_uid = head->so_uid;
+	if (head->so_cred) {
+		so->so_cred = head->so_cred;
+		so->so_cred->p_refcnt++;
+	} else so->so_cred = NULL;
 	(void) soreserve(so, head->so_snd.sb_hiwat, head->so_rcv.sb_hiwat);
 
 	if ((*so->so_proto->pr_usrreqs->pru_attach)(so, 0, NULL)) {
--- src/sys/netinet/ip_fw.c.orig	Tue Apr 20 10:29:59 1999
+++ src/sys/netinet/ip_fw.c	Wed Apr 21 00:20:50 1999
@@ -34,18 +34,21 @@
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
 #include <sys/kernel.h>
+#include <sys/proc.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
 #include <sys/sysctl.h>
+#include <sys/ucred.h>
 #include <net/if.h>
+#include <net/route.h>
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
+#include <netinet/in_pcb.h>
 #include <netinet/ip.h>
 #include <netinet/ip_var.h>
 #include <netinet/ip_icmp.h>
 #include <netinet/ip_fw.h>
 #ifdef DUMMYNET
-#include <net/route.h>
 #include <netinet/ip_dummynet.h>
 #endif
 #include <netinet/tcp.h>
@@ -53,6 +56,7 @@
 #include <netinet/tcp_var.h>
 #include <netinet/tcpip.h>
 #include <netinet/udp.h>
+#include <netinet/udp_var.h>
 
 #include <netinet/if_ether.h> /* XXX ethertype_ip */
 
@@ -608,13 +612,14 @@
 		if (f->fw_ipopt != f->fw_ipnopt && !ipopts_match(ip, f))
 			continue;
 
-		/* Check protocol; if wildcard, match */
-		if (f->fw_prot == IPPROTO_IP)
-			goto got_match;
-
-		/* If different, don't match */
-		if (ip->ip_p != f->fw_prot) 
-			continue;
+		/* Check protocol; if wildcard, and no uid, match */
+		if (f->fw_prot == IPPROTO_IP) {
+			if (!(f->fw_flg & (IP_FW_F_UID|IP_FW_F_GID)))
+				goto got_match;
+		} else
+		    /* If different, don't match */
+		    if (ip->ip_p != f->fw_prot) 
+			    continue;
 
 /*
  * here, pip==NULL for bridged pkts -- they include the ethernet
@@ -634,6 +639,88 @@
 			    }                                           \
 			} while (0)
 
+		/* Protocol specific checks for uid only */
+		if (f->fw_flg & (IP_FW_F_UID|IP_FW_F_GID)) {
+		    switch (ip->ip_p) {
+		    case IPPROTO_TCP:
+			{
+			    struct tcphdr *tcp;
+			    struct inpcb *P;
+
+			    if (offset == 1)	/* cf. RFC 1858 */
+				    goto bogusfrag;
+			    if (offset != 0)
+				    continue;
+
+			    PULLUP_TO(hlen + 14);
+			    tcp =(struct tcphdr *)((u_int32_t *)ip + ip->ip_hl);
+
+			    if (oif)
+				P = in_pcblookup_hash(&tcbinfo, ip->ip_dst,
+				   tcp->th_dport, ip->ip_src, tcp->th_sport, 0);
+			    else
+				P = in_pcblookup_hash(&tcbinfo, ip->ip_src,
+				   tcp->th_sport, ip->ip_dst, tcp->th_dport, 0);
+
+			    if (P && P->inp_socket && P->inp_socket->so_cred) {
+				if ((f->fw_flg & IP_FW_F_UID) &&
+				   P->inp_socket->so_cred->p_ruid != f->fw_uid)
+					continue;
+				if ((f->fw_flg & IP_FW_F_GID) &&
+				    !groupmember(f->fw_gid,
+				     P->inp_socket->so_cred->pc_ucred))
+					continue;
+			    } else continue;
+
+			    break;
+			}
+
+		    case IPPROTO_UDP:
+			{
+			    struct udphdr *udp;
+			    struct inpcb *P;
+
+			    if (offset != 0)
+				continue;
+
+			    PULLUP_TO(hlen + 4);
+			    udp =(struct udphdr *)((u_int32_t *)ip + ip->ip_hl);
+
+			    if (oif)
+				P = in_pcblookup_hash(&udbinfo, ip->ip_dst,
+				   udp->uh_dport, ip->ip_src, udp->uh_sport, 1);
+			    else
+				P = in_pcblookup_hash(&udbinfo, ip->ip_src,
+				   udp->uh_sport, ip->ip_dst, udp->uh_dport, 1);
+
+			    if (P && P->inp_socket && P->inp_socket->so_cred) {
+				if ((f->fw_flg & IP_FW_F_UID) &&
+				    P->inp_socket->so_cred->p_ruid != f->fw_uid)
+					continue;
+				if ((f->fw_flg & IP_FW_F_GID) &&
+				    !groupmember(f->fw_gid,
+				     P->inp_socket->so_cred->pc_ucred))
+					continue;
+			    } else continue;
+
+			    break;
+			}
+
+			default:
+				continue;
+/*
+ * XXX Shouldn't GCC be allowing two bogusfrag labels if they're both inside
+ * separate blocks? Hmm.... It seems it's got incorrect behavior here.
+ */
+#if 0
+bogusfrag:
+				if (fw_verbose)
+					ipfw_report(NULL, ip, rif, oif);
+				goto dropit;
+#endif
+			}
+		}
+		    
 		/* Protocol specific checks */
 		switch (ip->ip_p) {
 		case IPPROTO_TCP:
@@ -654,7 +741,8 @@
 
 				break;
 			}
-			PULLUP_TO(hlen + 14);
+			if (!(f->fw_flg & (IP_FW_F_UID|IP_FW_F_GID)))
+			    PULLUP_TO(hlen + 14);
 			tcp = (struct tcphdr *) ((u_int32_t *)ip + ip->ip_hl);
 			if (f->fw_tcpf != f->fw_tcpnf && !tcpflg_match(tcp, f))
 				continue;
@@ -673,12 +761,13 @@
 				 * rule specifies a port, we consider the rule
 				 * a non-match.
 				 */
-				if (f->fw_nports != 0)
+				if (f->fw_nports)
 					continue;
 
 				break;
 			}
-			PULLUP_TO(hlen + 4);
+			if (!(f->fw_flg & (IP_FW_F_UID|IP_FW_F_GID)))
+			    PULLUP_TO(hlen + 4);
 			udp = (struct udphdr *) ((u_int32_t *)ip + ip->ip_hl);
 			src_port = ntohs(udp->uh_sport);
 			dst_port = ntohs(udp->uh_dport);
@@ -1121,6 +1210,8 @@
 #ifdef IPFIREWALL_FORWARD
 	case IP_FW_F_FWD:
 #endif
+	case IP_FW_F_UID:
+	case IP_FW_F_GID:
 		break;
 	default:
 		dprintf(("%s invalid command\n", err_prefix));
--- src/sys/netinet/ip_fw.h.orig	Tue Apr 20 09:32:05 1999
+++ src/sys/netinet/ip_fw.h	Wed Apr 21 01:02:58 1999
@@ -81,6 +81,8 @@
 					/* count of 0 means match all ports) */
     void *pipe_ptr;                    /* Pipe ptr in case of dummynet pipe */
     void *next_rule_ptr ;              /* next rule in case of match */
+    uid_t fw_uid;			/* uid to match */
+    gid_t fw_gid;			/* gid to match */
 };
 
 #define IP_FW_GETNSRCP(rule)		((rule)->fw_nports & 0x0f)
@@ -144,7 +146,11 @@
 
 #define IP_FW_F_ICMPBIT 0x00100000	/* ICMP type bitmap is valid		*/
 
-#define IP_FW_F_MASK	0x001FFFFF	/* All possible flag bits mask		*/
+#define IP_FW_F_UID	0x00200000	/* filter by uid			*/
+
+#define IP_FW_F_GID	0x00400000	/* filter by uid			*/
+
+#define IP_FW_F_MASK	0x007FFFFF	/* All possible flag bits mask		*/
 
 /*
  * For backwards compatibility with rules specifying "via iface" but
--- src/sys/netinet/udp_var.h.orig      Sun Apr  4 22:47:21 1999
+++ src/sys/netinet/udp_var.h   Sun Apr  4 22:50:44 1999
@@ -103,6 +103,7 @@
 SYSCTL_DECL(_net_inet_udp);

 extern struct	pr_usrreqs udp_usrreqs;
+extern struct	inpcbinfo udbinfo;

 void	 udp_ctlinput __P((int, struct sockaddr *, void *));
 void	 udp_init __P((void)); 
--- src/sys/netinet/udp_usrreq.c.orig   Sun Apr  4 22:51:26 1999
+++ src/sys/netinet/udp_usrreq.c        Sun Apr  4 22:51:31 1999
@@ -78,7 +78,7 @@
	&log_in_vain, 0, "");

 static struct	inpcbhead udb;		/* from udp_var.h */
-static struct	inpcbinfo udbinfo;
+struct inpcbinfo udbinfo;

 #ifndef UDBHASHSIZE
 #define UDBHASHSIZE 16
--- src/sbin/ipfw/ipfw.8.orig	Fri Apr 23 22:26:55 1999
+++ src/sbin/ipfw/ipfw.8	Fri Apr 23 22:37:04 1999
@@ -258,6 +258,18 @@
 The search continues with the first rule numbered
 .Ar number
 or higher.
+.It Ar uid user
+Match all TCP or UDP packets sent by or received for a
+.Ar user .
+A
+.Ar user
+may be matched by name or identification number.
+.It Ar gid group
+Match all TCP or UDP packets sent by or received for a
+.Ar group .
+A
+.Ar group
+may be matched by name or identification number.
 .El
 .Pp
 If a packet matches more than one


>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.BSF.4.10.9905020016120.66712-100000>