Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 26 Mar 1997 16:18:16 -0600 (CST)
From:      tqbf@babel.enteract.com
To:        freebsd-security@freebsd.org
Subject:   More netinet suser() stuff...
Message-ID:  <19970326221816.19637.qmail@smtp.enteract.com>

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

An additional issue I'd like to bring up is the suser() requirement for
opening raw ICMP sockets. The same division of privilege should apply to
sending ICMP packets (thus removing the superuser requirement for ping and
traceroute) - in other words, a hole in traceroute shouldn't comprimise
anything but the ability to send and receive ICMP packets.

Of course, modifying restrictions to raw sockets is trickier than port
reservations, since there are many uses for raw sockets beyond ICMP. In
particular, unrestricted access to a raw IP socket allows users to create
*arbitrary* packets (using the "header included" socket option), which is
an unacceptable privilege to give away. 

The patch I'm including adds another two OIDs (rawicmp_uid and gid). Users
with those credentials can open raw sockets if the socket is created
specifying IPPROTO_ICMP. Reviewing the code involved (particularly the
ICMP protocol switch), I don't think checking the protocol is going to be
adequate to ensure that the socket is only used for ICMP. I'd appreciate
comments from people more familiar with the code than I.

I've used and tested this patch for about 2 hours here. Out of the box it
changes none of the privilege requirements for reserved ports or ICMP
sockets (you still need to run as root to do either), but will allow
through sysctl the ability to configure this. 

Thanks for listening!

*** kern/uipc_socket.c-pre-tqbf	Wed Mar 26 14:01:42 1997
--- kern/uipc_socket.c	Wed Mar 26 14:06:32 1997
***************
*** 54,57 ****
--- 54,59 ----
  SYSCTL_INT(_kern, KERN_SOMAXCONN, somaxconn, CTLFLAG_RW, &somaxconn, 0, "");
  
+ extern int in_okrawip(struct proc *p, struct protosw *prp);
+ 
  /*
   * Socket operation routines.
***************
*** 87,92 ****
--- 89,100 ----
  	TAILQ_INIT(&so->so_comp);
  	so->so_type = type;
+ 
+ 	/* XXX hack hack hack */
+ 
  	if (p->p_ucred->cr_uid == 0)
  		so->so_state = SS_PRIV;
+ 	else if(in_okrawip(p, prp)) 
+ 		so->so_state = SS_PRIV;
+ 
  	so->so_proto = prp;
  	error = (*prp->pr_usrreqs->pru_attach)(so, proto);

*** netinet/in.h-pre-tqbf	Tue Mar 25 23:46:16 1997
--- netinet/in.h	Wed Mar 26 14:11:29 1997
***************
*** 303,307 ****
  #define IPCTL_INTRQMAXLEN	10	/* max length of netisr queue */
  #define IPCTL_INTRQDROPS	11	/* number of netisr q drops */
! #define	IPCTL_MAXID		12
  
  #define	IPCTL_NAMES { \
--- 303,309 ----
  #define IPCTL_INTRQMAXLEN	10	/* max length of netisr queue */
  #define IPCTL_INTRQDROPS	11	/* number of netisr q drops */
! #define IPCTL_RESVUID		20	/* UID to bind privileged ports */
! #define IPCTL_RESVGID		21	/* GID to bind privileged ports */
! #define	IPCTL_MAXID		22
  
  #define	IPCTL_NAMES { \
*** netinet/icmp_var.h-pre-tqbf	Wed Mar 26 14:09:56 1997
--- netinet/icmp_var.h	Wed Mar 26 14:11:02 1997
***************
*** 62,66 ****
  #define	ICMPCTL_MASKREPL	1	/* allow replies to netmask requests */
  #define	ICMPCTL_STATS		2	/* statistics (read-only) */
! #define ICMPCTL_MAXID		3
  
  #define ICMPCTL_NAMES { \
--- 62,68 ----
  #define	ICMPCTL_MASKREPL	1	/* allow replies to netmask requests */
  #define	ICMPCTL_STATS		2	/* statistics (read-only) */
! #define ICMPCTL_RAWUID		3	/* UID to open raw ICMP socket */
! #define ICMPCTL_RAWGID		4	/* GID to open raw ICMP socket */
! #define ICMPCTL_MAXID		5
  
  #define ICMPCTL_NAMES { \
*** netinet/in_pcb.c-pre-tqbf	Tue Mar 25 23:09:13 1997
--- netinet/in_pcb.c	Wed Mar 26 14:13:42 1997
***************
*** 64,67 ****
--- 64,70 ----
  static void	 in_pcbinshash __P((struct inpcb *));
  static void	 in_rtchange __P((struct inpcb *, int));
+ static int	 in_okresvport __P((struct proc *));
+ 
+ int 	 cred_isingroup __P((gid_t, struct proc *));
  
  /*
***************
*** 189,194 ****
  			/* GROSS */
  			if (ntohs(lport) < IPPORT_RESERVED &&
! 			    (error = suser(p->p_ucred, &p->p_acflag)))
! 				return (EACCES);
  			t = in_pcblookup(inp->inp_pcbinfo, zeroin_addr, 0,
  			    sin->sin_addr, lport, wild);
--- 192,198 ----
  			/* GROSS */
  			if (ntohs(lport) < IPPORT_RESERVED &&
! 			   !in_okresvport(p))			
! 					return (EACCES);
! 
  			t = in_pcblookup(inp->inp_pcbinfo, zeroin_addr, 0,
  			    sin->sin_addr, lport, wild);
***************
*** 209,213 ****
  			lastport = &inp->inp_pcbinfo->lasthi;
  		} else if (inp->inp_flags & INP_LOWPORT) {
! 			if (error = suser(p->p_ucred, &p->p_acflag))
  				return (EACCES);
  			first = ipport_lowfirstauto;	/* 1023 */
--- 213,217 ----
  			lastport = &inp->inp_pcbinfo->lasthi;
  		} else if (inp->inp_flags & INP_LOWPORT) {
! 			if (!in_okresvport(p))
  				return (EACCES);
  			first = ipport_lowfirstauto;	/* 1023 */
***************
*** 747,749 ****
--- 751,802 ----
  	LIST_INSERT_HEAD(head, inp, inp_hash);
  	splx(s);
+ }
+ 
+ static int resv_uid;
+ static int resv_gid;
+ 
+ SYSCTL_INT(_net_inet_ip, 
+ 	   IPCTL_RESVUID, 
+ 	   resv_uid, 
+    	   CTLFLAG_RW, 
+ 	   &resv_uid,
+ 	   0,
+ 	   "");
+ 
+ SYSCTL_INT(_net_inet_ip, 
+ 	   IPCTL_RESVGID, 
+ 	   resv_gid, 
+    	   CTLFLAG_RW, 
+ 	   &resv_gid,
+ 	   0,
+ 	   "");
+ 
+ static int in_okresvport(struct proc *prc) {
+ 	int error;
+ 
+ 	error = suser(prc->p_ucred, &prc->p_acflag);
+ 	if(!error)
+ 		return(1);
+ 
+ 	if(resv_uid &&
+ 	   prc->p_ucred->cr_uid == resv_uid)
+ 		return(1);
+ 
+ 	if(resv_gid &&
+ 	   cred_isingroup((gid_t) resv_gid, prc))
+ 		return(1);
+ 
+ 	return(0);
+ }
+ 
+ int cred_isingroup(gid_t gid, struct proc *prc) {
+ 	struct ucred *uc = prc->p_ucred;
+ 	short i;
+ 	
+ 	for(i = 0; i < uc->cr_ngroups; i++) {
+ 		if(gid == uc->cr_groups[i])
+ 			return(1);
+ 	}
+ 
+ 	return(0);
  }
*** netinet/ip_icmp.c-pre-tqbf	Wed Mar 26 14:09:31 1997
--- netinet/ip_icmp.c	Wed Mar 26 14:41:04 1997
***************
*** 45,48 ****
--- 45,49 ----
  #include <sys/socket.h>
  #include <sys/sysctl.h>
+ #include <sys/proc.h>
  
  #include <net/if.h>
***************
*** 72,75 ****
--- 73,85 ----
  	&icmpmaskrepl, 0, "");
  
+ static int	rawicmp_uid  = 0;
+ static int	rawicmp_gid  = 0;
+ 
+ SYSCTL_INT(_net_inet_icmp, ICMPCTL_RAWUID, rawicmp_uid, CTLFLAG_RW,
+ 	&rawicmp_uid, 0, "");
+ 
+ SYSCTL_INT(_net_inet_icmp, ICMPCTL_RAWGID, rawicmp_gid, CTLFLAG_RW,
+ 	&rawicmp_gid, 0, "");
+ 
  #ifdef ICMPPRINTFS
  int	icmpprintfs = 0;
***************
*** 80,83 ****
--- 90,96 ----
  static int	ip_next_mtu __P((int, int));
  
+ int		in_okrawip __P((struct proc *, struct protosw *));
+ extern int	cred_isingroup(gid_t gid, struct proc *prc);
+ 
  extern	struct protosw inetsw[];
  
***************
*** 693,694 ****
--- 706,738 ----
  }
  #endif
+ 
+ int in_okrawip(struct proc *p, struct protosw *psw) {
+ 	int error;
+ 
+ 	/* superuser can always open raw sockets (redundant) */
+ 
+ 	error = suser(p->p_ucred, &p->p_acflag);
+ 	if(!error)
+ 		return(1);
+ 
+ 	/* only allow raw sockets for ICMP (this is probably 
+ 	 * a futile gesture, as I'm unsure that the kernel is 
+ 	 * tight enough internally to prevent arbitrary network
+ 	 * access, at least for sending packets, once a raw
+ 	 * socket is allocated).
+ 	 */
+ 
+ 	if(psw->pr_protocol != IPPROTO_ICMP)
+ 		return(0);
+ 
+ 	if(rawicmp_uid &&
+ 	   p->p_ucred->cr_uid == rawicmp_uid)
+ 		return(1);
+ 
+ 	if(rawicmp_gid &&
+ 	   cred_isingroup((gid_t) rawicmp_gid, p))
+ 		return(1);
+ 
+ 	return(0);
+ }
+ 




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?19970326221816.19637.qmail>