From owner-freebsd-security Wed Mar 26 14:18:39 1997 Return-Path: Received: (from root@localhost) by freefall.freebsd.org (8.8.5/8.8.5) id OAA03339 for security-outgoing; Wed, 26 Mar 1997 14:18:39 -0800 (PST) Received: from smtp.enteract.com (qmailr@char-star.rdist.org [206.54.252.22]) by freefall.freebsd.org (8.8.5/8.8.5) with SMTP id OAA03320 for ; Wed, 26 Mar 1997 14:18:32 -0800 (PST) Received: (qmail 19638 invoked by uid 1001); 26 Mar 1997 22:18:16 -0000 Message-ID: <19970326221816.19637.qmail@smtp.enteract.com> From: tqbf@babel.enteract.com Subject: More netinet suser() stuff... To: freebsd-security@freebsd.org Date: Wed, 26 Mar 1997 16:18:16 -0600 (CST) Reply-To: tqbf@enteract.com X-Mailer: ELM [version 2.4ME+ PL31 (25)] MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Sender: owner-security@freebsd.org X-Loop: FreeBSD.org Precedence: bulk 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 #include + #include #include *************** *** 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); + } +