Index: lib/libc/sys/chroot.2 =================================================================== --- lib/libc/sys/chroot.2 (revision 266749) +++ lib/libc/sys/chroot.2 (working copy) @@ -61,7 +61,13 @@ .Fn chroot has no effect on the process's current directory. .Pp -This call is restricted to the super-user. +By default, this call is restricted to the super-user. If +.Ql kern.chroot_allow_unprivileged +is set to a non-zero value, all users are capable of performing the +.Fn chroot +call. When called by an unprivileged user, the process and its children +won't honor the setuid and setgid bits when performing an +.Xr execve 2 . .Pp Depending on the setting of the .Ql kern.chroot_allow_open_directories @@ -141,6 +147,11 @@ open directories, or a MAC check), it is possible that this system call may return an error, with the working directory of the process left changed. +.Pp +When a call to +.Fn chroot +fails when invoked by an unprivileged user, the process is not properly +capable of executing setuid or setgid applications anymore. .Sh SECURITY CONSIDERATIONS The system have many hardcoded paths to files where it may load after the process starts. Index: sys/kern/kern_exec.c =================================================================== --- sys/kern/kern_exec.c (revision 266749) +++ sys/kern/kern_exec.c (working copy) @@ -695,7 +695,7 @@ ((oldcred->cr_flags & CRED_FLAG_CAPMODE) == 0) && #endif (imgp->vp->v_mount->mnt_flag & MNT_NOSUID) == 0 && - (p->p_flag & P_TRACED) == 0) { + (p->p_flag & P_TRACED) == 0 && (p->p_flag2 & P2_NOSUGID) == 0) { /* * Turn off syscall tracing for set-id programs, except for * root. Record any set-id flags first to make sure that Index: sys/kern/kern_fork.c =================================================================== --- sys/kern/kern_fork.c (revision 266749) +++ sys/kern/kern_fork.c (working copy) @@ -571,6 +571,7 @@ * been preserved. */ p2->p_flag |= p1->p_flag & P_SUGID; + p2->p_flag2 |= p1->p_flag2 & P2_NOSUGID; td2->td_pflags |= td->td_pflags & TDP_ALTSTACK; SESS_LOCK(p1->p_session); if (p1->p_session->s_ttyvp != NULL && p1->p_flag & P_CONTROLT) Index: sys/kern/vfs_syscalls.c =================================================================== --- sys/kern/vfs_syscalls.c (revision 266749) +++ sys/kern/vfs_syscalls.c (working copy) @@ -855,6 +855,11 @@ &chroot_allow_open_directories, 0, "Allow a process to chroot(2) if it has a directory open"); +/* This one allows unprivileged users to run chroot */ +static int chroot_allow_unprivileged = 0; +SYSCTL_INT(_kern, OID_AUTO, chroot_allow_unprivileged, CTLFLAG_RW, + &chroot_allow_unprivileged, 0, ""); + /* * Change notion of root (``/'') directory. */ @@ -871,11 +876,26 @@ } */ *uap; { struct nameidata nd; + struct proc *p; int error; error = priv_check(td, PRIV_VFS_CHROOT); - if (error != 0) - return (error); + if (error) { + if (!chroot_allow_unprivileged) + return (error); + + /* + * Disallow this process and its children to use setuid + * bits. Users could hardlink setuid applications into a + * chroot which contains a fake C library to obtain + * super-user privileges. + */ + p = td->td_proc; + PROC_LOCK(p); + p->p_flag2 |= P2_NOSUGID; + PROC_UNLOCK(p); + } + NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1, UIO_USERSPACE, uap->path, td); error = namei(&nd); Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h (revision 266749) +++ sys/sys/proc.h (working copy) @@ -646,6 +646,7 @@ /* These flags are kept in p_flag2. */ #define P2_INHERIT_PROTECTED 0x00000001 /* New children get P_PROTECTED. */ +#define P2_NOSUGID 0x00000002 /* Ignore set[ug]id on exec. */ /* * These were process status values (p_stat), now they are only used in