--- //depot/vendor/freebsd/src/sys/kern/vfs_syscalls.c 2005/09/01 21:50:32 +++ //depot/user/pjd/secure_syscalls/sys/kern/vfs_syscalls.c 2005/09/02 12:42:26 @@ -859,6 +859,50 @@ } /* + * Change notion of root (``/'') directory in a race-safe way. + */ +#ifndef _SYS_SYSPROTO_H_ +struct fchroot_args { + int fd; +}; +#endif +/* ARGSUSED */ +int +fchroot(td, uap) + struct thread *td; + struct fchroot_args /* { + int fd; + } */ *uap; +{ + struct nameidata nd; + struct vnode *vp; + struct file *fp; + int vfslocked; + int error; + + if ((error = suser_cred(td->td_ucred, PRISON_ROOT)) != 0) + return (error); + if ((error = getvnode(td->td_proc->p_fd, uap->fd, &fp)) != 0) + return (error); + vp = fp->f_vnode; + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + VREF(vp); + fdrop(fp, td); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); + error = change_dir(vp, td); +#ifdef MAC + if (error == 0) + error = mac_check_vnode_chroot(td->td_ucred, vp); +#endif + VOP_UNLOCK(vp, 0, td); + if (error == 0) + error = change_root(vp, td); + vrele(vp); + VFS_UNLOCK_GIANT(vfslocked); + return (error); +} + +/* * Common routine for chroot and chdir. Callers must provide a locked vnode * instance. */ @@ -1350,7 +1394,31 @@ { int error; - error = kern_link(td, uap->path, uap->link, UIO_USERSPACE); + error = kern_link(td, -1, uap->path, uap->link, UIO_USERSPACE); + return (error); +} + +/* + * Make a hard file link in a race-safe way. + */ +#ifndef _SYS_SYSPROTO_H_ +struct flink_args { + int fd; + char *link; +}; +#endif +/* ARGSUSED */ +int +flink(td, uap) + struct thread *td; + register struct flink_args /* { + int fd; + char *link; + } */ *uap; +{ + int error; + + error = kern_link(td, uap->fd, NULL, uap->link, UIO_USERSPACE); return (error); } @@ -1397,22 +1465,37 @@ } int -kern_link(struct thread *td, char *path, char *link, enum uio_seg segflg) +kern_link(struct thread *td, int fd, char *path, char *link, + enum uio_seg segflg) { - struct vnode *vp; + struct vnode *vp, *cvp; struct mount *mp; + struct file *fp; struct nameidata nd; int vfslocked; int lvfslocked; int error; + KASSERT((fd >= 0 && path == NULL) || (fd == -1 && path != NULL), + ("fd=%d, path=%p", fd, path)); bwillwrite(); - NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE, segflg, path, td); - if ((error = namei(&nd)) != 0) - return (error); - vfslocked = NDHASGIANT(&nd); - NDFREE(&nd, NDF_ONLY_PNBUF); - vp = nd.ni_vp; + if (path != NULL) { + NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE, segflg, path, td); + if ((error = namei(&nd)) != 0) + return (error); + vfslocked = NDHASGIANT(&nd); + NDFREE(&nd, NDF_ONLY_PNBUF); + vp = nd.ni_vp; + } else /* if (fd >= 0) */ { + if ((error = getvnode(td->td_proc->p_fd, fd, &fp)) != 0) { + printf("%s: getvnode(): %d\n", __func__, error); + return (error); + } + vp = fp->f_vnode; + vfslocked = VFS_LOCK_GIANT(vp->v_mount); + VREF(vp); + fdrop(fp, td); + } if (vp->v_type == VDIR) { vrele(vp); VFS_UNLOCK_GIANT(vfslocked); @@ -1618,18 +1701,43 @@ { int error; - error = kern_unlink(td, uap->path, UIO_USERSPACE); + error = kern_unlink(td, -1, uap->path, UIO_USERSPACE); + return (error); +} + +/* + * Delete a name from the filesystem in a race-safe way. + */ +#ifndef _SYS_SYSPROTO_H_ +struct funlink_args { + int fd; + char *path; +}; +#endif +/* ARGSUSED */ +int +funlink(td, uap) + struct thread *td; + struct funlink_args /* { + int fd; + char *path; + } */ *uap; +{ + int error; + + error = kern_unlink(td, uap->fd, uap->path, UIO_USERSPACE); return (error); } int -kern_unlink(struct thread *td, char *path, enum uio_seg pathseg) +kern_unlink(struct thread *td, int fd, char *path, enum uio_seg pathseg) { struct mount *mp; - struct vnode *vp; - int error; + struct vnode *vp, *cvp; + struct file *fp; struct nameidata nd; int vfslocked; + int error; restart: bwillwrite(); @@ -1638,6 +1746,21 @@ return (error); vfslocked = NDHASGIANT(&nd); vp = nd.ni_vp; + if (fd >= 0) { + error = getvnode(td->td_proc->p_fd, fd, &fp); + if (error != 0) + printf("%s: getvnode(): %d\n", __func__, error); + else { + cvp = fp->f_vnode; + fdrop(fp, td); + if (vp != cvp) { + printf("%s: vnode comparsion failed.\n", __func__); + error = ENOENT; + } + } + if (error != 0) + goto out1; + } if (vp->v_type == VDIR) error = EPERM; /* POSIX */ else { @@ -1676,6 +1799,7 @@ #endif vn_finished_write(mp); } +out1: NDFREE(&nd, NDF_ONLY_PNBUF); if (vp == nd.ni_dvp) vrele(vp); @@ -3164,17 +3288,44 @@ } */ *uap; { - return (kern_rename(td, uap->from, uap->to, UIO_USERSPACE)); + return (kern_rename(td, -1, uap->from, uap->to, UIO_USERSPACE)); +} + +/* + * Rename files in a race-safe way. + */ +#ifndef _SYS_SYSPROTO_H_ +struct frename_args { + int fd; + char *from; + char *to; +}; +#endif +/* ARGSUSED */ +int +frename(td, uap) + struct thread *td; + register struct rename_args /* { + int fd; + char *from; + char *to; + } */ *uap; +{ + int error; + + error = kern_rename(td, uap->fd, uap->from, uap->to, UIO_USERSPACE); + return (error); } int -kern_rename(struct thread *td, char *from, char *to, enum uio_seg pathseg) +kern_rename(struct thread *td, int fd, char *from, char *to, + enum uio_seg pathseg) { struct mount *mp = NULL; - struct vnode *tvp, *fvp, *tdvp; + struct vnode *tvp, *fvp, *tdvp, *cvp; + struct file *fp; struct nameidata fromnd, tond; - int tvfslocked; - int fvfslocked; + int tvfslocked, fvfslocked; int error; bwillwrite(); @@ -3189,13 +3340,29 @@ return (error); fvfslocked = NDHASGIANT(&fromnd); tvfslocked = 0; + fvp = fromnd.ni_vp; + if (fd >= 0) { + error = getvnode(td->td_proc->p_fd, fd, &fp); + if (error != 0) + printf("%s: getvnode(): %d\n", __func__, error); + else { + cvp = fp->f_vnode; + fdrop(fp, td); + if (fvp != cvp) { + printf("%s: vnode comparsion failed.\n", + __func__); + error = ENOENT; + } + } + } #ifdef MAC - error = mac_check_vnode_rename_from(td->td_ucred, fromnd.ni_dvp, - fromnd.ni_vp, &fromnd.ni_cnd); + if (error == 0) { + error = mac_check_vnode_rename_from(td->td_ucred, fromnd.ni_dvp, + fvp, &fromnd.ni_cnd); + } VOP_UNLOCK(fromnd.ni_dvp, 0, td); - VOP_UNLOCK(fromnd.ni_vp, 0, td); + VOP_UNLOCK(fvp, 0, td); #endif - fvp = fromnd.ni_vp; if (error == 0) error = vn_start_write(fvp, &mp, V_WAIT | PCATCH); if (error != 0) {