MFC jail: Change both root and working directories in jail_attach(2)

jail_attach(2) performs an internal chroot operation, leaving it up to
the calling process to assure the working directory is inside the jail.

Add a matching internal chdir operation to the jail's root.  Also
ignore kern.chroot_allow_open_directories, and always disallow the
operation if there are any directory descriptors open.

Approved by:	so
Security:	CVE-2020-25582
Security:	FreeBSD-SA-21:05.jail_chdir
Reported by:    mjg
Approved by:    markj, kib

(cherry picked from commit d4380c0cdd)
(cherry picked from commit ca9ab8ea17748a1758701fde262cb272fb757989)
This commit is contained in:
Jamie Gritton 2021-02-19 14:13:35 -08:00 committed by Franco Fichtner
parent 8549a1d34e
commit 672f84f97b
4 changed files with 42 additions and 6 deletions

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd February 8, 2012
.Dd February 19, 2021
.Dt JAIL 2
.Os
.Sh NAME
@ -228,6 +228,9 @@ The
system call attaches the current process to an existing jail,
identified by
.Fa jid .
It changes the process's root and current directories to the jail's
.Va path
directory.
.Pp
The
.Fn jail_remove

View File

@ -3169,10 +3169,9 @@ chroot_refuse_vdir_fds(struct filedesc *fdp)
}
/*
* Common routine for kern_chroot() and jail_attach(). The caller is
* responsible for invoking priv_check() and mac_vnode_check_chroot() to
* authorize this operation.
*/
* The caller is responsible for invoking priv_check() and
* mac_vnode_check_chroot() to authorize this operation.
*/
int
pwd_chroot(struct thread *td, struct vnode *vp)
{
@ -3218,6 +3217,39 @@ pwd_chdir(struct thread *td, struct vnode *vp)
vrele(oldvp);
}
/*
* jail_attach(2) changes both root and working directories.
*/
int
pwd_chroot_chdir(struct thread *td, struct vnode *vp)
{
struct filedesc *fdp;
struct vnode *oldvrp, *oldvcp;
int error;
fdp = td->td_proc->p_fd;
FILEDESC_XLOCK(fdp);
error = chroot_refuse_vdir_fds(fdp);
if (error != 0) {
FILEDESC_XUNLOCK(fdp);
return (error);
}
oldvrp = fdp->fd_rdir;
vrefact(vp);
fdp->fd_rdir = vp;
oldvcp = fdp->fd_cdir;
vrefact(vp);
fdp->fd_cdir = vp;
if (fdp->fd_jdir == NULL) {
vrefact(vp);
fdp->fd_jdir = vp;
}
FILEDESC_XUNLOCK(fdp);
vrele(oldvrp);
vrele(oldvcp);
return (0);
}
/*
* Scan all active processes and prisons to see if any of them have a current
* or root directory of `olddp'. If so, replace them with the new mount point.

View File

@ -2427,7 +2427,7 @@ do_jail_attach(struct thread *td, struct prison *pr)
goto e_unlock;
#endif
VOP_UNLOCK(pr->pr_root, 0);
if ((error = pwd_chroot(td, pr->pr_root)))
if ((error = pwd_chroot_chdir(td, pr->pr_root)))
goto e_revert_osd;
newcred = crget();

View File

@ -243,6 +243,7 @@ fd_modified(struct filedesc *fdp, int fd, seq_t seq)
/* cdir/rdir/jdir manipulation functions. */
void pwd_chdir(struct thread *td, struct vnode *vp);
int pwd_chroot(struct thread *td, struct vnode *vp);
int pwd_chroot_chdir(struct thread *td, struct vnode *vp);
void pwd_ensure_dirs(void);
#endif /* _KERNEL */