diff options
author | Edward Tomasz Napierala <trasz@FreeBSD.org> | 2021-07-20 08:56:04 +0000 |
---|---|---|
committer | Edward Tomasz Napierala <trasz@FreeBSD.org> | 2022-02-14 18:42:21 +0000 |
commit | 460b4b550dc9619f33a0d1af3870eaef60b04797 (patch) | |
tree | b7bbcc372632d035ea5e5679ef569aac45b278c2 | |
parent | c428292cb3768d913eda6d546ece59379b6277d5 (diff) | |
download | src-460b4b550dc9.tar.gz src-460b4b550dc9.zip |
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)
-rw-r--r-- | sys/kern/vfs_syscalls.c | 17 | ||||
-rw-r--r-- | usr.sbin/chroot/chroot.8 | 13 | ||||
-rw-r--r-- | 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); } |