From owner-freebsd-security Sun Sep 14 23:53:42 1997 Return-Path: Received: (from root@localhost) by hub.freebsd.org (8.8.7/8.8.7) id XAA06070 for security-outgoing; Sun, 14 Sep 1997 23:53:42 -0700 (PDT) Received: from plum.cyber.com.au (plum.cyber.com.au [203.7.155.24]) by hub.freebsd.org (8.8.7/8.8.7) with SMTP id XAA06059 for ; Sun, 14 Sep 1997 23:53:35 -0700 (PDT) Received: (from darrenr@localhost) by plum.cyber.com.au (8.6.12/8.6.6) id QAA05056 for security@freebsd.org; Mon, 15 Sep 1997 16:53:31 +1000 From: Darren Reed Message-Id: <199709150653.QAA05056@plum.cyber.com.au> Subject: OpenBSD Security Advisory: BSD I/O Signals (fwd) To: security@freebsd.org Date: Mon, 15 Sep 1997 16:53:29 +1000 (EST) Priority: urgent X-Mailer: ELM [version 2.4 PL23] Content-Type: text Sender: owner-freebsd-security@freebsd.org X-Loop: FreeBSD.org Precedence: bulk In some mail I received from tqbf@rdist.org, sie wrote > From tech-security-owner-darrenr=cyber.com.au@NetBSD.ORG Mon Sep 15 16:10:11 1997 > Message-ID: <19970915060810.20028.qmail@smtp.enteract.com> > From: tqbf@rdist.org > Subject: OpenBSD Security Advisory: BSD I/O Signals > To: tech-security@NetBSD.ORG > Date: Mon, 15 Sep 1997 01:08:10 -0500 (CDT) > 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: tech-security-owner@NetBSD.ORG > Precedence: list > Delivered-To: tech-security@NetBSD.ORG > > ---------------------------------------------------------------------------- > > OpenBSD Security Advisory > > September 15, 1997 > > Vulnerability in I/O Signal Handling > > ---------------------------------------------------------------------------- > > SYNOPSIS > > A vulnerability discovered in the 4.4BSD kernel allows unprivileged users > to send certain signals to arbitrary processes on the system. Depending on > the operating system and targeted program, this may allow users to kill > off processes or disrupt the operation of certain programs. > > ---------------------------------------------------------------------------- > > AFFECTED SYSTEMS > > This vulnerability has been tested on all available 4.4BSD-based operating > systems, including BSDI, NetBSD, OpenBSD, and FreeBSD, in their most > recent release revisions. Additionally, this problem is known to affect > SGI IRIX, and may affect other operating systems as well. > > ---------------------------------------------------------------------------- > > DETAILS > > Certain programs implemented in Unix operating systems make use of a > facility called "asynchronous I/O" to handle multiple tasks > simultaneously. Asynchronous I/O allows a process to be notified whenever > it has data that needs to be read from an input source; the kernel > notifies the process using a signal. > > Asynchronous I/O is enabled on a descriptor using the fcntl() system call; > a descriptor with the O_ASYNC flag set will cause a signal to be sent > whenever there is data available to be read from it. Additionally, using > the FIOASYNC ioctl(), asynchronous notification can be enabled on a > descriptor. > > In cases where multiple processes are used in an application, it becomes > useful to allow a descriptor to notify other processes as well. This is > accomplished by use of another fcntl() operation, F_SETOWN, as well as an > ioctl, FIOSETOWN (certain devices also provide an interface to this > facility with the TIOCSPGRP ioctl). These operations allow a program to > set the process or process group that will receive signal notification of > pending I/O. > > A lack of checking in the code that handles these operations allows a > program to specify an arbitrary process ID when using a socket or device > file descriptor. By setting the recipient of signal notification to a > process that is not owned by the program, the kernel can be tricked into > signalling arbitrary programs. > > Additionally, vulnerable kernels do not keep track of the credentials > associated with a process when determining whether to send I/O signals; > because of this, it is possible to specify the PID of a process that is > owned by an attacker, and then destroy that process and wait for it's PID > to be re-used. The new process occupying that PID can then be signalled by > the attacker, regardless of it's owner. > > It's important to note that operating systems that check credentials when > a program attempts to set the PID for I/O notification (thus evading part > of this vulnerability) may still be vulnerable to the latter problem > (process ID re-use), if credentials aren't checked at signal delivery > time. We recommend that concerned parties contact their operating system > vendors or support channels to verify their vulnerability status. > > ---------------------------------------------------------------------------- > > TECHNICAL DETAILS > > This vulnerability exists due to a lack of credential checking in the > kernel when setting the recipient of I/O notification. BSD-based > kernels maintain this information in descriptor-specific data structures > unrelated to the process table; the vulnerability discussed here involves > sockets, which maintain the signal recipient in the per-descriptor > "socket" structure, although certain devices provide similar facilities > and vulnerabilities. > > On 4.4BSD systems, the signal normally sent to inform a process of pending > I/O is SIGIO. The default disposition of the SIGIO signal is "ignore" - > thus, most processes are unaffected by this vulnerability. However, > certain programs explicitly catch SIGIO in order to use asynchronous I/O; > these programs can be disrupted by sending stray SIGIO's. > > The process notification information is also used to determine the process > that receives notification of out-of-band data on a socket. The same > vulnerability applies, this time by setting the process to notify and then > sending a message with the MSG_OOB flag set; the targetted process will > receive a SIGURG signal. Certain network daemons (ftpd, for instance) can > be disrupted by being sent a stray SIGURG signal when no data is available > for reading. > > The problem is more serious on vulnerable System V operating systems; in > many cases, SIGIO is the equivalent of SIGPOLL, and the default > disposition of that signal is "terminate process". On SGI's IRIX operating > system, exploitation of this vulnerability can kill any process on the > system. > > In addition to being an extremely potent denial of service attack, > surgical application of this vulnerability can be used to compromise the > system - for example, a process holding a bound address (NFS port 2049, > for instance) can be killed off and it's port stolen; this can be used to > steal NFS file handles. > > In addition to sockets, some devices also provide facilities for > notification of pending I/O; examples include the "log", "tun", and "bpf". > The BPF and tunnel devices are of minimal concern, as they are not > typically accessible by arbitrary users (although BPF is interesting in > that it will allow the owner of a bpf-associated descriptor to choose an > arbitrary signal to send, including SIGSTOP). > > Unfortunately, the log device is normally accessible by users, and can be > used to perform the same attack as sockets allow. It's also worth noting > that the interface that allows programs to set the process to receive > notification of I/O on the log device renders the legitimate purpose of > this facility totally unreliable; unrelated programs can seize control of > the asynchronous I/O notification on the log device, causing programs that > rely on it to fail. The provided patches do not attempt to resolve this > problem. > > ---------------------------------------------------------------------------- > > RESOLUTION > > This is a kernel problem that can only be fixed by patching the > problematic system code. Patches for the OpenBSD operating system are > provided in this advisory; FreeBSD is known to be working on a similar > resolution. > > The provided OpenBSD patch causes the kernel to keep track of the > credentials of the process associated with an I/O object; the credentials > are checked whenever I/O notification will occur, and therefore resolve > both the F_SETOWN and PID-reuse problems. Device drivers that present an > interface to I/O notification must be modified to check credentials when > the TIOCSPGRP (or equivalent) ioctl() is used to set notificatio PID; the > OpenBSD patch resolves all currently known occurances of this in that > operating system. > > ---------------------------------------------------------------------------- > > CREDITS > > This vulnerability is believed to have been discovered originally by Alan > Peakall. Documentation and testing of this problem was conducted by Theo > de Raadt and the OpenBSD development team; SGI information was obtained > from Timothy Newsham. > > The OpenBSD patch for this vulnerability was written in a caffeinated haze > by Theo de Raadt of the OpenBSD project. > > The developers at OpenBSD would like to thank Perry Metzger for his > continuous support of their work. > > ---------------------------------------------------------------------------- > > OPENBSD PATCH > > Index: sys/signalvar.h > =================================================================== > RCS file: /cvs/src/sys/sys/signalvar.h,v > retrieving revision 1.6 > retrieving revision 1.7 > diff -u -r1.6 -r1.7 > --- signalvar.h 1997/02/01 21:49:36 1.6 > +++ signalvar.h 1997/08/31 20:42:01 1.7 > @@ -156,6 +156,7 @@ > int coredump __P((struct proc *p)); > void execsigs __P((struct proc *p)); > void gsignal __P((int pgid, int sig)); > +void csignal __P((pid_t pgid, int signum, uid_t uid, uid_t euid)); > int issignal __P((struct proc *p)); > void pgsignal __P((struct pgrp *pgrp, int sig, int checkctty)); > void postsig __P((int sig)); > Index: sys/socketvar.h > =================================================================== > RCS file: /cvs/src/sys/sys/socketvar.h,v > retrieving revision 1.10 > retrieving revision 1.11 > diff -u -r1.10 -r1.11 > --- socketvar.h 1997/02/28 04:04:13 1.10 > +++ socketvar.h 1997/08/31 20:42:02 1.11 > @@ -71,6 +71,8 @@ > short so_timeo; /* connection timeout */ > u_short so_error; /* error affecting connection */ > pid_t so_pgid; /* pgid for signals */ > + uid_t so_siguid; /* uid of process who set so_pgid */ > + uid_t so_sigeuid; /* euid of process who set so_pgid */ > u_long so_oobmark; /* chars to oob mark */ > /* > * Variables for socket buffering. > Index: kern/kern_descrip.c > =================================================================== > RCS file: /cvs/src/sys/kern/kern_descrip.c,v > retrieving revision 1.13 > retrieving revision 1.14 > diff -u -r1.13 -r1.14 > --- kern_descrip.c 1997/08/21 05:17:37 1.13 > +++ kern_descrip.c 1997/08/31 20:42:15 1.14 > @@ -55,6 +55,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -251,8 +252,11 @@ > > case F_SETOWN: > if (fp->f_type == DTYPE_SOCKET) { > - ((struct socket *)fp->f_data)->so_pgid = > - (long)SCARG(uap, arg); > + struct socket *so = (struct socket *)fp->f_data; > + > + so->so_pgid = (long)SCARG(uap, arg); > + so->so_siguid = p->p_cred->p_ruid; > + so->so_sigeuid = p->p_ucred->cr_uid; > return (0); > } > if ((long)SCARG(uap, arg) <= 0) { > Index: kern/kern_sig.c > =================================================================== > RCS file: /cvs/src/sys/kern/kern_sig.c,v > retrieving revision 1.16 > retrieving revision 1.17 > diff -u -r1.16 -r1.17 > --- kern_sig.c 1997/02/01 21:49:41 1.16 > +++ kern_sig.c 1997/08/31 20:42:18 1.17 > @@ -481,6 +481,46 @@ > } > } > return (nfound ? 0 : ESRCH); > +} > + > +#define CANDELIVER(uid, euid, p) \ > + (euid == 0 || \ > + (uid) == (p)->p_cred->p_ruid || \ > + (uid) == (p)->p_cred->p_svuid || \ > + (uid) == (p)->p_ucred->cr_uid || \ > + (euid) == (p)->p_cred->p_ruid || \ > + (euid) == (p)->p_cred->p_svuid || \ > + (euid) == (p)->p_ucred->cr_uid) > + > +/* > + * Deliver signum to pgid, but first check uid/euid against each > + * process and see if it is permitted. > + */ > +void > +csignal(pgid, signum, uid, euid) > + pid_t pgid; > + int signum; > + uid_t uid, euid; > +{ > + struct pgrp *pgrp; > + struct proc *p; > + > + if (pgid == 0) > + return; > + if (pgid < 0) { > + pgid = -pgid; > + if ((pgrp = pgfind(pgid)) == NULL) > + return; > + for (p = pgrp->pg_members.lh_first; p; > + p = p->p_pglist.le_next) > + if (CANDELIVER(uid, euid, p)) > + psignal(p, signum); > + } else { > + if ((p = pfind(pgid)) == NULL) > + return; > + if (CANDELIVER(uid, euid, p)) > + psignal(p, signum); > + } > } > > /* > Index: kern/subr_log.c > =================================================================== > RCS file: /cvs/src/sys/kern/subr_log.c,v > retrieving revision 1.3 > retrieving revision 1.4 > diff -u -r1.3 -r1.4 > --- subr_log.c 1996/04/21 22:27:17 1.3 > +++ subr_log.c 1997/08/31 20:42:20 1.4 > @@ -60,6 +60,8 @@ > int sc_state; /* see above for possibilities */ > struct selinfo sc_selp; /* process waiting on select call */ > int sc_pgid; /* process/group for async I/O */ > + uid_t sc_siguid; /* uid for process that set sc_pgid */ > + uid_t sc_sigeuid; /* euid for process that set sc_pgid */ > } logsoftc; > > int log_open; /* also used in log() */ > @@ -179,17 +181,12 @@ > void > logwakeup() > { > - struct proc *p; > - > if (!log_open) > return; > selwakeup(&logsoftc.sc_selp); > - if (logsoftc.sc_state & LOG_ASYNC) { > - if (logsoftc.sc_pgid < 0) > - gsignal(-logsoftc.sc_pgid, SIGIO); > - else if ((p = pfind(logsoftc.sc_pgid)) != NULL) > - psignal(p, SIGIO); > - } > + if (logsoftc.sc_state & LOG_ASYNC) > + csignal(logsoftc.sc_pgid, SIGIO, > + logsoftc.sc_siguid, logsoftc.sc_sigeuid); > if (logsoftc.sc_state & LOG_RDWAIT) { > wakeup((caddr_t)msgbufp); > logsoftc.sc_state &= ~LOG_RDWAIT; > @@ -232,6 +229,8 @@ > > case TIOCSPGRP: > logsoftc.sc_pgid = *(int *)data; > + logsoftc.sc_siguid = p->p_cred->p_ruid; > + logsoftc.sc_sigeuid = p->p_ucred->cr_uid; > break; > > case TIOCGPGRP: > Index: kern/sys_generic.c > =================================================================== > RCS file: /cvs/src/sys/kern/sys_generic.c,v > retrieving revision 1.7 > retrieving revision 1.8 > diff -u -r1.7 -r1.8 > --- sys_generic.c 1997/01/27 23:21:13 1.7 > +++ sys_generic.c 1997/08/31 20:42:21 1.8 > @@ -480,7 +480,11 @@ > case FIOSETOWN: > tmp = *(int *)data; > if (fp->f_type == DTYPE_SOCKET) { > - ((struct socket *)fp->f_data)->so_pgid = tmp; > + struct socket *so = (struct socket *)fp->f_data; > + > + so->so_pgid = tmp; > + so->so_siguid = p->p_cred->p_ruid; > + so->so_sigeuid = p->p_ucred->cr_uid; > error = 0; > break; > } > Index: kern/sys_socket.c > =================================================================== > RCS file: /cvs/src/sys/kern/sys_socket.c,v > retrieving revision 1.2 > retrieving revision 1.3 > diff -u -r1.2 -r1.3 > --- sys_socket.c 1997/02/24 14:19:59 1.2 > +++ sys_socket.c 1997/08/31 20:42:23 1.3 > @@ -39,6 +39,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -112,6 +113,8 @@ > > case SIOCSPGRP: > so->so_pgid = *(int *)data; > + so->so_siguid = p->p_cred->p_ruid; > + so->so_sigeuid = p->p_ucred->cr_uid; > return (0); > > case SIOCGPGRP: > Index: kern/uipc_socket.c > =================================================================== > RCS file: /cvs/src/sys/kern/uipc_socket.c,v > retrieving revision 1.15 > retrieving revision 1.17 > diff -u -r1.15 -r1.17 > --- uipc_socket.c 1997/06/29 18:14:35 1.15 > +++ uipc_socket.c 1997/08/31 20:42:24 1.17 > @@ -1058,11 +1060,6 @@ > sohasoutofband(so) > register struct socket *so; > { > - struct proc *p; > - > - if (so->so_pgid < 0) > - gsignal(-so->so_pgid, SIGURG); > - else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0) > - psignal(p, SIGURG); > + csignal(so->so_pgid, SIGURG, so->so_siguid, so->so_sigeuid); > selwakeup(&so->so_rcv.sb_sel); > } > Index: kern/uipc_socket2.c > =================================================================== > RCS file: /cvs/src/sys/kern/uipc_socket2.c,v > retrieving revision 1.5 > retrieving revision 1.6 > diff -u -r1.5 -r1.6 > --- uipc_socket2.c 1997/02/21 08:45:00 1.5 > +++ uipc_socket2.c 1997/08/31 20:42:26 1.6 > @@ -315,20 +315,14 @@ > register struct socket *so; > register struct sockbuf *sb; > { > - struct proc *p; > - > selwakeup(&sb->sb_sel); > sb->sb_flags &= ~SB_SEL; > if (sb->sb_flags & SB_WAIT) { > sb->sb_flags &= ~SB_WAIT; > wakeup((caddr_t)&sb->sb_cc); > } > - if (so->so_state & SS_ASYNC) { > - if (so->so_pgid < 0) > - gsignal(-so->so_pgid, SIGIO); > - else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0) > - psignal(p, SIGIO); > - } > + if (so->so_state & SS_ASYNC) > + csignal(so->so_pgid, SIGIO, so->so_siguid, so->so_sigeuid); > } > > /* > Index: net/bpf.c > =================================================================== > RCS file: /cvs/src/sys/net/bpf.c,v > retrieving revision 1.9 > retrieving revision 1.10 > diff -u -r1.9 -r1.10 > --- bpf.c 1997/03/17 16:29:37 1.9 > +++ bpf.c 1997/08/31 20:42:29 1.10 > @@ -522,14 +522,10 @@ > bpf_wakeup(d) > register struct bpf_d *d; > { > - struct proc *p; > - > wakeup((caddr_t)d); > if (d->bd_async && d->bd_sig) > - if (d->bd_pgid > 0) > - gsignal (d->bd_pgid, d->bd_sig); > - else if ((p = pfind (-d->bd_pgid)) != NULL) > - psignal (p, d->bd_sig); > + csignal(d->bd_pgid, d->bd_sig, > + d->bd_siguid, d->bd_sigeuid); > > #if BSD >= 199103 > selwakeup(&d->bd_sel); > @@ -822,6 +818,8 @@ > */ > case TIOCSPGRP: /* Process or group to send signals to */ > d->bd_pgid = *(int *)addr; > + d->bd_siguid = p->p_cred->p_ruid; > + d->bd_sigeuid = p->p_ucred->cr_uid; > break; > > case TIOCGPGRP: > Index: net/bpfdesc.h > =================================================================== > RCS file: /cvs/src/sys/net/bpfdesc.h,v > retrieving revision 1.2 > retrieving revision 1.3 > diff -u -r1.2 -r1.3 > --- bpfdesc.h 1997/02/24 13:33:56 1.2 > +++ bpfdesc.h 1997/08/31 20:42:30 1.3 > @@ -77,6 +77,8 @@ > int bd_async; /* non-zero if packet reception should generate signal */ > int bd_sig; /* signal to send upon packet reception */ > pid_t bd_pgid; /* process or group id for signal */ > + uid_t bd_siguid; /* uid for process that set pgid */ > + uid_t bd_sigeuid; /* euid for process that set pgid */ > #if BSD < 199103 > u_char bd_selcoll; /* true if selects collide */ > int bd_timedout; > Index: net/if_tun.c > =================================================================== > RCS file: /cvs/src/sys/net/if_tun.c,v > retrieving revision 1.19 > retrieving revision 1.20 > diff -u -r1.19 -r1.20 > --- if_tun.c 1997/07/29 07:18:20 1.19 > +++ if_tun.c 1997/08/31 20:42:32 1.20 > @@ -84,7 +84,9 @@ > struct tun_softc { > u_short tun_flags; /* misc flags */ > struct ifnet tun_if; /* the interface */ > - int tun_pgrp; /* the process group - if any */ > + pid_t tun_pgid; /* the process group - if any */ > + uid_t tun_siguid; /* uid for process that set tun_pgid */ > + uid_t tun_sigeuid; /* euid for process that set tun_pgid */ > struct selinfo tun_rsel; /* read select */ > struct selinfo tun_wsel; /* write select (not used) */ > }; > @@ -228,7 +230,7 @@ > } > splx(s); > } > - tp->tun_pgrp = 0; > + tp->tun_pgid = 0; > selwakeup(&tp->tun_rsel); > > TUNDEBUG(("%s: closed\n", ifp->if_xname)); > @@ -331,7 +333,6 @@ > { > struct tun_softc *tp = ifp->if_softc; > struct tunnel_header *th; > - struct proc *p; > int s; > > TUNDEBUG(("%s: tun_output\n", ifp->if_xname)); > @@ -371,12 +372,9 @@ > tp->tun_flags &= ~TUN_RWAIT; > wakeup((caddr_t)tp); > } > - if (tp->tun_flags & TUN_ASYNC && tp->tun_pgrp) { > - if (tp->tun_pgrp > 0) > - gsignal(tp->tun_pgrp, SIGIO); > - else if ((p = pfind(-tp->tun_pgrp)) != NULL) > - psignal(p, SIGIO); > - } > + if (tp->tun_flags & TUN_ASYNC && tp->tun_pgid) > + csignal(tp->tun_pgid, SIGIO, > + tp->tun_siguid, tp->tun_sigeuid); > selwakeup(&tp->tun_rsel); > return 0; > } > @@ -446,10 +444,12 @@ > splx(s); > break; > case TIOCSPGRP: > - tp->tun_pgrp = *(int *)data; > + tp->tun_pgid = *(int *)data; > + tp->tun_siguid = p->p_cred->p_ruid; > + tp->tun_sigeuid = p->p_ucred->cr_uid; > break; > case TIOCGPGRP: > - *(int *)data = tp->tun_pgrp; > + *(int *)data = tp->tun_pgid; > break; > default: > splx(s); > > >