git: 460b4b550dc9 - stable/13 - Implement unprivileged chroot
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 18 Feb 2022 14:52:23 UTC
The branch stable/13 has been updated by trasz: URL: https://cgit.FreeBSD.org/src/commit/?id=460b4b550dc9619f33a0d1af3870eaef60b04797 commit 460b4b550dc9619f33a0d1af3870eaef60b04797 Author: Edward Tomasz Napierala <trasz@FreeBSD.org> AuthorDate: 2021-07-20 08:56:04 +0000 Commit: Edward Tomasz Napierala <trasz@FreeBSD.org> CommitDate: 2022-02-14 18:42:21 +0000 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 (cherry picked from commit a40cf4175c90142442d0c6515f6c83956336699b) --- sys/kern/vfs_syscalls.c | 17 +++++++++++++++-- usr.sbin/chroot/chroot.8 | 13 ++++++++++++- usr.sbin/chroot/chroot.c | 20 +++++++++++++++++--- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 3d1e7d9c73fa..19a32a175895 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -956,6 +956,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. */ @@ -968,11 +972,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 .Os .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. +If +.Dv security.bsd.unprivileged_chroot +sysctl is set to 1, it will make it possible to chroot without +superuser privileges. .El .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); }