6

src - FreeBSD source tree

 2 years ago
source link: https://cgit.freebsd.org/src/commit/?id=a40cf4175c90142442d0c6515f6c83956336699b
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
FreeBSD source tree index : src FreeBSD source tree
Implement unprivileged chroot

This builds on recently introduced NO_NEW_PRIVS flag to implement unprivileged chroot, enabled by `security.bsd.unprivileged_chroot`. It allows non-root processes to chroot(2), provided they have the NO_NEW_PRIVS flag set.

The chroot(8) utility gets a new flag, -n, which sets NO_NEW_PRIVS before chrooting.

Reviewed By: kib Sponsored By: EPSRC Relnotes: yes Differential Revision: https://reviews.freebsd.org/D30130

3 files changed, 44 insertions, 6 deletions
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 0b80faa5a27d..82629a4f5947 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -955,6 +955,10 @@ kern_chdir(struct thread *td, const char *path, enum uio_seg pathseg)
return (0);
+static int unprivileged_chroot = 0;
+SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_chroot, CTLFLAG_RW,
+ &unprivileged_chroot, 0,
+ "Unprivileged processes can use chroot(2)");
* Change notion of root (``/'') directory.
@@ -967,11 +971,20 @@ int
sys_chroot(struct thread *td, struct chroot_args *uap)
struct nameidata nd;
+ struct proc *p;
int error;
error = priv_check(td, PRIV_VFS_CHROOT);
- if (error != 0)
- return (error);
+ if (error != 0) {
+ p = td->td_proc;
+ PROC_LOCK(p);
+ if (unprivileged_chroot == 0 ||
+ (p->p_flag2 & P2_NO_NEW_PRIVS) == 0) {
+ PROC_UNLOCK(p);
+ return (error);
+ PROC_UNLOCK(p);
NDINIT(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | AUDITVNODE1,
UIO_USERSPACE, uap->path, td);
error = namei(&nd);
diff --git a/usr.sbin/chroot/chroot.8 b/usr.sbin/chroot/chroot.8
index 977961abb6ec..9f5b2f380376 100644
--- a/usr.sbin/chroot/chroot.8
+++ b/usr.sbin/chroot/chroot.8
@@ -28,7 +28,7 @@
.\" @(#)chroot.8 8.1 (Berkeley) 6/9/93
.\" $FreeBSD$
-.Dd June 27, 2020
+.Dd July 20, 2021
.Dt CHROOT 8
.Sh NAME
@@ -39,6 +39,7 @@
.Op Fl G Ar group Ns Op Cm \&, Ns Ar group ...
.Op Fl g Ar group
.Op Fl u Ar user
+.Op Fl n
.Ar newroot
.Op Ar command Op Ar arg ...
.Sh DESCRIPTION
@@ -61,6 +62,16 @@ Run the command with the permissions of the specified
.It Fl u Ar user
Run the command as the
.Ar user .
+.It Fl n
+Use the
+.Dv PROC_NO_NEW_PRIVS_CTL
+.Xr procctl 2
+command before chrooting, effectively disabling SUID/SGID bits
+for the calling process and its descendants.
+.Dv security.bsd.unprivileged_chroot
+sysctl is set to 1, it will make it possible to chroot without
+superuser privileges.
.Sh ENVIRONMENT
The following environment variable is referenced by
diff --git a/usr.sbin/chroot/chroot.c b/usr.sbin/chroot/chroot.c
index 60ef631ca875..bb87ae6f0503 100644
--- a/usr.sbin/chroot/chroot.c
+++ b/usr.sbin/chroot/chroot.c
@@ -44,6 +44,7 @@ static char sccsid[] = "@(#)chroot.c 8.1 (Berkeley) 6/9/93";
__FBSDID("$FreeBSD$");
#include <sys/types.h>
+#include <sys/procctl.h>
#include <ctype.h>
#include <err.h>
@@ -51,6 +52,7 @@ __FBSDID("$FreeBSD$");
#include <limits.h>
#include <paths.h>
#include <pwd.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -67,13 +69,15 @@ main(int argc, char *argv[])
const char *shell;
gid_t gid, *gidlist;
uid_t uid;
- int ch, gids;
+ int arg, ch, error, gids;
long ngroups_max;
+ bool nonpriviledged;
gid = 0;
uid = 0;
user = group = grouplist = NULL;
- while ((ch = getopt(argc, argv, "G:g:u:")) != -1) {
+ nonpriviledged = false;
+ while ((ch = getopt(argc, argv, "G:g:u:n")) != -1) {
switch(ch) {
case 'u':
user = optarg;
@@ -90,6 +94,9 @@ main(int argc, char *argv[])
if (*grouplist == '\0')
usage();
break;
+ case 'n':
+ nonpriviledged = true;
+ break;
case '?':
default:
usage();
@@ -153,6 +160,13 @@ main(int argc, char *argv[])
+ if (nonpriviledged) {
+ arg = PROC_NO_NEW_PRIVS_ENABLE;
+ error = procctl(P_PID, getpid(), PROC_NO_NEW_PRIVS_CTL, &arg);
+ if (error != 0)
+ err(1, "procctl");
if (chdir(argv[0]) == -1 || chroot(".") == -1)
err(1, "%s", argv[0]);
@@ -179,6 +193,6 @@ static void
usage(void)
(void)fprintf(stderr, "usage: chroot [-g group] [-G group,group,...] "
- "[-u user] newroot [command]\n");
+ "[-u user] [-n ] newroot [command]\n");
exit(1);

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK