Synopsis: Procfs can give any users a rootshell NetBSD versions: NetBSD 1.4.1 and before, NetBSD-current to 20000126 Thanks to: Jason Thorpe, Charles Hannum Reported in NetBSD Security Advisory: SA2000-001 *** sys/kern/kern_subr.c.orig Wed Mar 24 06:51:25 1999 --- sys/kern/kern_subr.c Fri Jan 28 22:48:05 2000 *************** *** 392,397 **** --- 392,463 ---- } /* + * Exec hook code. + */ + + struct exechook_desc { + LIST_ENTRY(exechook_desc) ehk_list; + void (*ehk_fn) __P((struct proc *, void *)); + void *ehk_arg; + }; + + LIST_HEAD(, exechook_desc) exechook_list; + + void * + exechook_establish(fn, arg) + void (*fn) __P((struct proc *, void *)); + void *arg; + { + struct exechook_desc *edp; + + edp = (struct exechook_desc *) + malloc(sizeof(*edp), M_DEVBUF, M_NOWAIT); + if (edp == NULL) + return NULL; + + edp->ehk_fn = fn; + edp->ehk_arg = arg; + LIST_INSERT_HEAD(&exechook_list, edp, ehk_list); + + return (edp); + } + + void + exechook_disestablish(vhook) + void *vhook; + { + #ifdef DIAGNOSTIC + struct exechook_desc *edp; + + for (edp = exechook_list.lh_first; edp != NULL; + edp = edp->ehk_list.le_next) + if (edp == vhook) + break; + if (edp == NULL) + panic("exechook_disestablish: hook not established"); + #endif + + LIST_REMOVE((struct exechook_desc *)vhook, ehk_list); + free(vhook, M_DEVBUF); + } + + /* + * Run exec hooks. + */ + void + doexechooks(p) + struct proc *p; + { + struct exechook_desc *edp; + + for (edp = LIST_FIRST(&exechook_list); + edp != NULL; + edp = LIST_NEXT(edp, ehk_list)) { + (*edp->ehk_fn)(p, edp->ehk_arg); + } + } + + /* * Determine the root device and, if instructed to, the root file system. */ *** sys/kern/kern_exec.c.orig Fri Jan 28 21:50:37 2000 --- sys/kern/kern_exec.c Fri Jan 28 22:48:05 2000 *************** *** 467,472 **** --- 467,474 ---- p->p_cred->p_svuid = p->p_ucred->cr_uid; p->p_cred->p_svgid = p->p_ucred->cr_gid; + doexechooks(p); + uvm_km_free_wakeup(exec_map, (vaddr_t) argp, NCARGS); FREE(nid.ni_cnd.cn_pnbuf, M_NAMEI); *** sys/miscfs/procfs/procfs_vfsops.c.orig Sat Feb 27 00:44:46 1999 --- sys/miscfs/procfs/procfs_vfsops.c Fri Jan 28 22:48:05 2000 *************** *** 57,62 **** --- 57,63 ---- #include #include #include + #include #include #include /* for PAGE_SIZE */ *************** *** 91,96 **** --- 92,98 ---- struct proc *p; { size_t size; + struct procfsmount *pmnt; if (UIO_MX & (UIO_MX-1)) { log(LOG_ERR, "procfs: invalid directory entry size"); *************** *** 101,113 **** return (EOPNOTSUPP); mp->mnt_flag |= MNT_LOCAL; ! mp->mnt_data = 0; vfs_getnewfsid(mp, MOUNT_PROCFS); (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN, &size); memset(mp->mnt_stat.f_mntonname + size, 0, MNAMELEN - size); memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN); memcpy(mp->mnt_stat.f_mntfromname, "procfs", sizeof("procfs")); return (0); } --- 103,122 ---- return (EOPNOTSUPP); mp->mnt_flag |= MNT_LOCAL; ! pmnt = (struct procfsmount *) malloc(sizeof(struct procfsmount), ! M_UFSMNT, M_WAITOK); /* XXX need new malloc type */ ! ! mp->mnt_data = (qaddr_t)pmnt; vfs_getnewfsid(mp, MOUNT_PROCFS); (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN, &size); memset(mp->mnt_stat.f_mntonname + size, 0, MNAMELEN - size); memset(mp->mnt_stat.f_mntfromname, 0, MNAMELEN); memcpy(mp->mnt_stat.f_mntfromname, "procfs", sizeof("procfs")); + + pmnt->pmnt_exechook = exechook_establish(procfs_revoke_vnodes, mp); + pmnt->pmnt_mp = mp; + return (0); } *************** *** 129,134 **** --- 138,148 ---- if ((error = vflush(mp, 0, flags)) != 0) return (error); + exechook_disestablish(VFSTOPROC(mp)->pmnt_exechook); + + free(mp->mnt_data, M_UFSMNT); + mp->mnt_data = 0; + return (0); } *************** *** 255,260 **** --- 269,275 ---- void procfs_init() { + procfs_hashinit(); } int *** sys/miscfs/procfs/procfs.h.orig Wed Mar 24 06:51:27 1999 --- sys/miscfs/procfs/procfs.h Fri Jan 28 22:48:05 2000 *************** *** 62,68 **** * control data for the proc file system. */ struct pfsnode { ! struct pfsnode *pfs_next; /* next on list */ struct vnode *pfs_vnode; /* vnode associated with this pfsnode */ pfstype pfs_type; /* type of procfs node */ pid_t pfs_pid; /* associated process */ --- 62,68 ---- * control data for the proc file system. */ struct pfsnode { ! LIST_ENTRY(pfsnode) pfs_hash; /* hash chain */ struct vnode *pfs_vnode; /* vnode associated with this pfsnode */ pfstype pfs_type; /* type of procfs node */ pid_t pfs_pid; /* associated process */ *************** *** 89,94 **** --- 89,102 ---- ((type) + 2) : \ ((((pid)+1) << 4) + ((int) (type)))) + struct procfsmount { + void *pmnt_exechook; + struct mount *pmnt_mp; + }; + + #define VFSTOPROC(mp) ((struct procfsmount *)(mp)->mnt_data) + #define PROCTOVFS(pp) ((pp)->pmnt_mp) + /* * Convert between pfsnode vnode */ *************** *** 126,131 **** --- 134,141 ---- struct uio *)); int procfs_checkioperm __P((struct proc *, struct proc *)); + void procfs_revoke_vnodes __P((struct proc *, void *)); + void procfs_hashinit __P((void)); /* functions to check whether or not files should be displayed */ int procfs_validfile __P((struct proc *)); *** sys/miscfs/procfs/procfs_subr.c.orig Fri Mar 12 19:45:40 1999 --- sys/miscfs/procfs/procfs_subr.c Fri Feb 25 23:29:30 2000 *************** *** 51,59 **** #include ! static struct pfsnode *pfshead; ! static int pfsvplock; #define ISSET(t, f) ((t) & (f)) /* --- 51,67 ---- #include ! void procfs_hashins __P((struct pfsnode *)); ! void procfs_hashrem __P((struct pfsnode *)); ! struct vnode *procfs_hashget __P((pid_t, pfstype, struct mount *)); ! ! LIST_HEAD(pfs_hashhead, pfsnode) *pfs_hashtbl; ! u_long pfs_ihash; /* size of hash table - 1 */ ! #define PFSPIDHASH(pid) (&pfs_hashtbl[(pid) & pfs_ihash]) + struct lock pfs_hashlock; + struct simplelock pfs_hash_slock; + #define ISSET(t, f) ((t) & (f)) /* *************** *** 91,131 **** { struct pfsnode *pfs; struct vnode *vp; - struct pfsnode **pp; int error; ! loop: ! for (pfs = pfshead; pfs != 0; pfs = pfs->pfs_next) { ! vp = PFSTOV(pfs); ! if (pfs->pfs_pid == pid && ! pfs->pfs_type == pfs_type && ! vp->v_mount == mp) { ! if (vget(vp, 0)) ! goto loop; ! *vpp = vp; return (0); ! } ! } ! /* ! * otherwise lock the vp list while we call getnewvnode ! * since that can block. ! */ ! if (pfsvplock & PROCFS_LOCKED) { ! pfsvplock |= PROCFS_WANT; ! sleep((caddr_t) &pfsvplock, PINOD); ! goto loop; } - pfsvplock |= PROCFS_LOCKED; - - if ((error = getnewvnode(VT_PROCFS, mp, procfs_vnodeop_p, vpp)) != 0) - goto out; vp = *vpp; MALLOC(pfs, void *, sizeof(struct pfsnode), M_TEMP, M_WAITOK); vp->v_data = pfs; - pfs->pfs_next = 0; pfs->pfs_pid = (pid_t) pid; pfs->pfs_type = pfs_type; pfs->pfs_vnode = vp; --- 99,121 ---- { struct pfsnode *pfs; struct vnode *vp; int error; ! do { ! if ((*vpp = procfs_hashget(pid, pfs_type, mp)) != NULL) return (0); ! } while (lockmgr(&pfs_hashlock, LK_EXCLUSIVE|LK_SLEEPFAIL, 0)); ! if ((error = getnewvnode(VT_PROCFS, mp, procfs_vnodeop_p, vpp)) != 0) { ! *vpp = NULL; ! lockmgr(&pfs_hashlock, LK_RELEASE, 0); ! return (error); } vp = *vpp; MALLOC(pfs, void *, sizeof(struct pfsnode), M_TEMP, M_WAITOK); vp->v_data = pfs; pfs->pfs_pid = (pid_t) pid; pfs->pfs_type = pfs_type; pfs->pfs_vnode = vp; *************** *** 175,192 **** panic("procfs_allocvp"); } ! /* add to procfs vnode list */ ! for (pp = &pfshead; *pp; pp = &(*pp)->pfs_next) ! continue; ! *pp = pfs; ! ! out: ! pfsvplock &= ~PROCFS_LOCKED; ! ! if (pfsvplock & PROCFS_WANT) { ! pfsvplock &= ~PROCFS_WANT; ! wakeup((caddr_t) &pfsvplock); ! } return (error); } --- 165,172 ---- panic("procfs_allocvp"); } ! procfs_hashins(pfs); ! lockmgr(&pfs_hashlock, LK_RELEASE, 0); return (error); } *************** *** 195,209 **** procfs_freevp(vp) struct vnode *vp; { - struct pfsnode **pfspp; struct pfsnode *pfs = VTOPFS(vp); ! for (pfspp = &pfshead; *pfspp != 0; pfspp = &(*pfspp)->pfs_next) { ! if (*pfspp == pfs) { ! *pfspp = pfs->pfs_next; ! break; ! } ! } FREE(vp->v_data, M_TEMP); vp->v_data = 0; --- 175,183 ---- procfs_freevp(vp) struct vnode *vp; { struct pfsnode *pfs = VTOPFS(vp); ! procfs_hashrem(pfs); FREE(vp->v_data, M_TEMP); vp->v_data = 0; *************** *** 332,335 **** --- 306,394 ---- return (nm); return (0); + } + + /* + * Initialize pfsnode hash table. + */ + void + procfs_hashinit() + { + lockinit(&pfs_hashlock, PINOD, "pfs_hashlock", 0, 0); + pfs_hashtbl = hashinit(desiredvnodes / 4, M_UFSMNT, M_WAITOK, + &pfs_ihash); + simple_lock_init(&pfs_hash_slock); + } + + struct vnode * + procfs_hashget(pid, type, mp) + pid_t pid; + pfstype type; + struct mount *mp; + { + struct pfsnode *pp; + struct vnode *vp; + + loop: + simple_lock(&pfs_hash_slock); + for (pp = PFSPIDHASH(pid)->lh_first; pp; pp = pp->pfs_hash.le_next) { + vp = PFSTOV(pp); + if (pid == pp->pfs_pid && pp->pfs_type == type && + vp->v_mount == mp) { + simple_unlock(&pfs_hash_slock); + if (vget(vp, 0)) + goto loop; + return (vp); + } + } + simple_unlock(&pfs_hash_slock); + return (NULL); + } + + /* + * Insert the pfsnode into the hash table and lock it. + */ + void + procfs_hashins(pp) + struct pfsnode *pp; + { + struct pfs_hashhead *ppp; + + simple_lock(&pfs_hash_slock); + ppp = PFSPIDHASH(pp->pfs_pid); + LIST_INSERT_HEAD(ppp, pp, pfs_hash); + simple_unlock(&pfs_hash_slock); + } + + /* + * Remove the pfsnode from the hash table. + */ + void + procfs_hashrem(pp) + struct pfsnode *pp; + { + simple_lock(&pfs_hash_slock); + LIST_REMOVE(pp, pfs_hash); + simple_unlock(&pfs_hash_slock); + } + + void + procfs_revoke_vnodes(p, arg) + struct proc *p; + void *arg; + { + struct pfsnode *pfs, *pnext; + struct vnode *vp; + struct mount *mp = (struct mount *)arg; + + if (!(p->p_flag & P_SUGID)) + return; + + for (pfs = PFSPIDHASH(p->p_pid)->lh_first; pfs; pfs = pnext) { + vp = PFSTOV(pfs); + pnext = pfs->pfs_hash.le_next; + if (vp->v_usecount > 0 && pfs->pfs_pid == p->p_pid && + vp->v_mount == mp) + VOP_REVOKE(vp, REVOKEALL); + } } *** sys/sys/systm.h.orig Fri Jan 28 21:51:11 2000 --- sys/sys/systm.h Fri Jan 28 22:48:05 2000 *************** *** 275,280 **** --- 275,288 ---- void mountroothook_destroy __P((void)); void domountroothook __P((void)); + /* + * Exec hooks. Subsystems may want to do cleanup when a process + * execs. + */ + void *exechook_establish __P((void (*)(struct proc *, void *), void *)); + void exechook_disestablish __P((void *)); + void doexechooks __P((struct proc *)); + int uiomove __P((void *, int, struct uio *)); #ifdef _KERNEL