Add a sysctl variable which can help stop chroot(2) escapes.

kern.chroot_allow_open_directories = 0
	chroot(2) fails if there are open directories.

kern.chroot_allow_open_directories = 1 (default)
	chroot(2) fails if there are open directories and the process
	is subject of a previous chroot(2).

kern.chroot_allow_open_directories = anything else
	filedescriptors are not checked.  (old behaviour).

I'm very interested in reports about software which breaks when
running with the default setting.
This commit is contained in:
Poul-Henning Kamp 1999-03-23 14:26:40 +00:00
parent 7f4173cc09
commit cc7532aaf0
3 changed files with 119 additions and 4 deletions

View file

@ -60,7 +60,33 @@ It should be noted that
has no effect on the process's current directory.
.Pp
This call is restricted to the super-user.
.Sh RETURN VALUES
.Pp
Depending on the setting of the
.Ql kern.chroot_allow_open_directories
sysctl variable, open filedescriptors which reference directories
will make the
.Fn chroot
fail as follows:
.Pp
If
.Ql kern.chroot_allow_open_directories
is set to zero,
.Fn chroot
will always fail with EPERM if there are any directories open.
.Pp
If
.Ql kern.chroot_allow_open_directories
is set to one (the default),
.Fn chroot
will fail with EPERM if there are any directories open and the
process is already subject to a
.Fn chroot
call.
.Pp
Any other value for
.Ql kern.chroot_allow_open_directories
will bypass the check for open directories
.Pp
Upon successful completion, a value of 0 is returned. Otherwise,
a value of -1 is returned and
.Va errno
@ -72,7 +98,8 @@ will fail and the root directory will be unchanged if:
.It Bq Er ENOTDIR
A component of the path name is not a directory.
.It Bq Er EPERM
The effective user ID is not the super-user.
The effective user ID is not the super-user, or one or more
filedescriptors are open directories.
.It Bq Er ENAMETOOLONG
A component of a pathname exceeded 255 characters,
or an entire path name exceeded 1023 characters.

View file

@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94
* $Id: vfs_syscalls.c,v 1.119 1999/02/27 07:06:05 julian Exp $
* $Id: vfs_syscalls.c,v 1.120 1999/03/03 02:35:51 julian Exp $
*/
/* For 4.3 integer FS ID compatibility */
@ -71,6 +71,7 @@
static int change_dir __P((struct nameidata *ndp, struct proc *p));
static void checkdirs __P((struct vnode *olddp));
static int chroot_refuse_vdir_fds __P((struct filedesc *fdp));
static int setfown __P((struct proc *, struct vnode *, uid_t, gid_t));
static int setfmode __P((struct proc *, struct vnode *, int));
static int setfflags __P((struct proc *, struct vnode *, int));
@ -827,6 +828,44 @@ chdir(p, uap)
return (0);
}
/*
* Helper function for raised chroot(2) security function: Refuse if
* any filedescriptors are open directories.
*/
static int
chroot_refuse_vdir_fds(fdp)
struct filedesc *fdp;
{
struct vnode *vp;
struct file *fp;
int error;
int fd;
for (fd = 0; fd < fdp->fd_nfiles ; fd++) {
error = getvnode(fdp, fd, &fp);
if (error)
continue;
vp = (struct vnode *)fp->f_data;
if (vp->v_type != VDIR)
continue;
return(EPERM);
}
return (0);
}
/*
* This sysctl determines if we will allow a process to chroot(2) if it
* has a directory open:
* 0: disallowed for all processes.
* 1: allowed for processes that were not already chroot(2)'ed.
* 2: allowed for all processes.
*/
static int chroot_allow_open_directories = 1;
SYSCTL_INT(_kern, OID_AUTO, chroot_allow_open_directories, CTLFLAG_RW,
&chroot_allow_open_directories, 0, "");
/*
* Change notion of root (``/'') directory.
*/
@ -848,6 +887,11 @@ chroot(p, uap)
struct nameidata nd;
error = suser(p->p_ucred, &p->p_acflag);
if (error)
return (error);
if (chroot_allow_open_directories == 0 ||
(chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode))
error = chroot_refuse_vdir_fds(fdp);
if (error)
return (error);
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,

View file

@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* @(#)vfs_syscalls.c 8.13 (Berkeley) 4/15/94
* $Id: vfs_syscalls.c,v 1.119 1999/02/27 07:06:05 julian Exp $
* $Id: vfs_syscalls.c,v 1.120 1999/03/03 02:35:51 julian Exp $
*/
/* For 4.3 integer FS ID compatibility */
@ -71,6 +71,7 @@
static int change_dir __P((struct nameidata *ndp, struct proc *p));
static void checkdirs __P((struct vnode *olddp));
static int chroot_refuse_vdir_fds __P((struct filedesc *fdp));
static int setfown __P((struct proc *, struct vnode *, uid_t, gid_t));
static int setfmode __P((struct proc *, struct vnode *, int));
static int setfflags __P((struct proc *, struct vnode *, int));
@ -827,6 +828,44 @@ chdir(p, uap)
return (0);
}
/*
* Helper function for raised chroot(2) security function: Refuse if
* any filedescriptors are open directories.
*/
static int
chroot_refuse_vdir_fds(fdp)
struct filedesc *fdp;
{
struct vnode *vp;
struct file *fp;
int error;
int fd;
for (fd = 0; fd < fdp->fd_nfiles ; fd++) {
error = getvnode(fdp, fd, &fp);
if (error)
continue;
vp = (struct vnode *)fp->f_data;
if (vp->v_type != VDIR)
continue;
return(EPERM);
}
return (0);
}
/*
* This sysctl determines if we will allow a process to chroot(2) if it
* has a directory open:
* 0: disallowed for all processes.
* 1: allowed for processes that were not already chroot(2)'ed.
* 2: allowed for all processes.
*/
static int chroot_allow_open_directories = 1;
SYSCTL_INT(_kern, OID_AUTO, chroot_allow_open_directories, CTLFLAG_RW,
&chroot_allow_open_directories, 0, "");
/*
* Change notion of root (``/'') directory.
*/
@ -848,6 +887,11 @@ chroot(p, uap)
struct nameidata nd;
error = suser(p->p_ucred, &p->p_acflag);
if (error)
return (error);
if (chroot_allow_open_directories == 0 ||
(chroot_allow_open_directories == 1 && fdp->fd_rdir != rootvnode))
error = chroot_refuse_vdir_fds(fdp);
if (error)
return (error);
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,