From b792258e1f76c04a5bb61f60e7121a362b38a00b Mon Sep 17 00:00:00 2001 From: Richard Maw Date: Wed, 18 Apr 2012 16:59:11 +0100 Subject: autogen: remove dependency on which Signed-off-by: Colin Walters --- autogen.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/autogen.sh b/autogen.sh index 6035bc0..4674ca8 100755 --- a/autogen.sh +++ b/autogen.sh @@ -6,9 +6,8 @@ test -n "$srcdir" || srcdir=. olddir=`pwd` cd $srcdir -AUTORECONF=`which autoreconf` -if test -z $AUTORECONF; then - echo "*** No autoreconf found, please intall it ***" +if ! (autoreconf --version >/dev/null 2>&1); then + echo "*** No autoreconf found, please install it ***" exit 1 fi -- cgit v1.2.1 From c689880fe2a2523ca8b4d8e31a22cbf91b7d5047 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Tue, 24 Apr 2012 07:58:56 -0400 Subject: Add manual page Commit message and build rules written by Colin Walters . Reviewed-by: Colin Walters --- Makefile-docbook-man.am | 33 ---------------- Makefile-stub.am | 1 + Makefile-user-chroot.am | 4 ++ Makefile.am | 3 -- configure.ac | 7 +++- doc/linux-user-chroot.8 | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 113 insertions(+), 38 deletions(-) delete mode 100644 Makefile-docbook-man.am create mode 100644 doc/linux-user-chroot.8 diff --git a/Makefile-docbook-man.am b/Makefile-docbook-man.am deleted file mode 100644 index 4bdb501..0000000 --- a/Makefile-docbook-man.am +++ /dev/null @@ -1,33 +0,0 @@ -# Docbook generation copied from systemd/Makefile.am -# -# Copyright 2010 Lennart Poettering -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# systemd is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with systemd; If not, see . - -XML_FILES = \ - ${patsubst %.1,%.xml,${patsubst %.3,%.xml,${patsubst %.5,%.xml,${patsubst %.7,%.xml,${patsubst %.8,%.xml,$(MANPAGES)}}}}} -EXTRA_DIST += $(XML_FILES) - -dist_man_MANS = $(MANPAGES) - -XSLTPROC_FLAGS = \ - --nonet \ - --param funcsynopsis.style "'ansi'" - -XSLTPROC_PROCESS_MAN = \ - $(AM_V_GEN)$(MKDIR_P) $(dir $@) && \ - $(XSLTPROC) -o $@ $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< - -doc/%.1: doc/%.xml - $(XSLTPROC_PROCESS_MAN) diff --git a/Makefile-stub.am b/Makefile-stub.am index fe4b88b..236aaaa 100644 --- a/Makefile-stub.am +++ b/Makefile-stub.am @@ -28,6 +28,7 @@ bin_PROGRAMS = sbin_PROGRAMS = bin_SCRIPTS = sbin_SCRIPTS = +dist_man_MANS = libexec_PROGRAMS = noinst_LTLIBRARIES = noinst_PROGRAMS = diff --git a/Makefile-user-chroot.am b/Makefile-user-chroot.am index c3801f5..32db975 100644 --- a/Makefile-user-chroot.am +++ b/Makefile-user-chroot.am @@ -28,3 +28,7 @@ endif linux_user_chroot_newnet_SOURCES = src/linux-user-chroot-newnet.c linux_user_chroot_newnet_CFLAGS = $(AM_CFLAGS) + +if BUILD_DOCUMENTATION +dist_man_MANS += doc/linux-user-chroot.8 +endif diff --git a/Makefile.am b/Makefile.am index af22885..741cdd0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,6 +17,3 @@ include Makefile-stub.am include Makefile-user-chroot.am -if HAVE_XSLTPROC -include Makefile-docbook-man.am -endif diff --git a/configure.ac b/configure.ac index da5863f..d2c6ded 100644 --- a/configure.ac +++ b/configure.ac @@ -26,8 +26,11 @@ AC_CHECK_HEADER([linux/securebits.h], [AC_DEFINE([HAVE_LINUX_SECUREBITS_H], [1], [Define to 1 if we have securebits.h])]) -AC_PATH_PROG([XSLTPROC], [xsltproc]) -AM_CONDITIONAL(HAVE_XSLTPROC, test x"$XSLTPROC" != x) +AC_ARG_ENABLE(documentation, + AC_HELP_STRING([--enable-documentation], + [build documentation]),, + enable_documentation=yes) +AM_CONDITIONAL(BUILD_DOCUMENTATION, test x$enable_documentation = xyes) AC_ARG_ENABLE(newnet-helper, AC_HELP_STRING([--enable-newnet-helper], diff --git a/doc/linux-user-chroot.8 b/doc/linux-user-chroot.8 new file mode 100644 index 0000000..f1cae55 --- /dev/null +++ b/doc/linux-user-chroot.8 @@ -0,0 +1,103 @@ +.\" Copyright 2012 Codethink Limited +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it would be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software Foundation, +.\" Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +.\" +.TH LINUX-USER-CHROOT 8 +.SH NAME +linux\-user\-chroot \- safely allow normal users to chroot +.SH SYNOPSIS +.B linux\-user\-chroot +.RB [ --unshare-ipc ] +.RB [ --unshare-pid ] +.RB [ --unshare-net ] +.RB [ --mount-proc " \fIDIR\fR] +.RB [ --mount-readonly " \fIDIR\fR"] +.RB [ --mount-bind " \fISOURCE DEST\fR"] +.RB [ --chdir " \fIDIR\fR"] +.I ROOTDIR +.I PROGRAM +.IR ARGS... +.SH DESCRIPTION +.B linux\-user\-chroot +is a tool meant for building software in a clean environment. +The user needs to create a directory tree with the build dependencies needed, +and only those, +and then +.B linux\-user\-chroot +runs the actual build commands such that the commands only see the directory +tree. +This is useful for ensuring the build gets the right version of its build +dependencies, for example. +.PP +.B linux\-user\-chroot +works similary to +.BR chroot (8), +but does not require the caller to have root privileges. +It uses Linux containers to restrict the chroot to make this safe. +The command run inside the chroot is run as the calling user, not as root. +.PP +.B linux\-user\-chroot +executes a command, and sets the root directory for the command to the +directory specified by the user +.RI ( ROOTDIR ). +Additionally, it creates a "nosuid" bind mount over the root filesystem, +to prevent the build from gaining privileges using setuid binaries. +The command can further be restricted from accessing the network, +and it can be set up with new process ID and SysV IPC namespaces. +.SH OPTIONS +.TP +.BR \-\-unshare\-ipc +Create a new SysV IPC namespace for the command. +.TP +.BR \-\-unshare\-pid +Create a new process ID (PID) namespace for the command. +This prevents the command from seeing any other processes in the system, +except itself and the processes it itself creates. +.TP +.BR \-\-unshare\-net +Create a new, empty networking stack. +This prevents the command from using any networking, +including loopback. +.TP +.BI \-\-mount\-proc " DIR" +Mount the proc filesystem at +.IR DIR . +.TP +.BI \-\-mount\-readonly " DIR" +Make +.I DIR +be read-only for the command. +.TP +.BI \-\-mount\-bind " SOURCE DEST" +Add a bind mount while the command is executing. +.TP +.BI \-\-chdir " DIR" +After setting the new root directory for the command, +change the current working directory to be +.IR DIR . +.SH "EXIT STATUS" +The exit status is the exit status of the executed command, +or 1 if +.B linux\-user\-chroot +failed to execute the command. +.SH EXAMPLE +To build software in the real system, but without networking: +.IP +.nf +linux\-user\-chroot \-\-unshare\-net \-\-chdir "$(pwd)" +make clean all check +.fi +.SH "SEE ALSO" +.BR chroot (8). -- cgit v1.2.1 From 89e30f023676530525414ed41afb261f6baf5529 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 24 Apr 2012 08:37:28 -0400 Subject: README: Improve --- README | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/README b/README index bab4b9b..a107280 100644 --- a/README +++ b/README @@ -1,13 +1,23 @@ -Motivation ----------- +Summary +------- + +This tool allows regular (non-root) users to call chroot(2), create +Linux bind mounts, and use some Linux container features. It's +primarily intended for use by build systems. + +Project information +------------------- -It's really useful for build systems to be able to call chroot(2) as a -regular (non-root) user. +There's no web page yet; send patches to +Colin Walters -First, it ensures that the build isn't picking up files it shouldn't -be. This helps avoid the problem of "host contamination", where -e.g. we want libfoo.h from inside our root, not the one outside the -root. +Why is this useful? +------------------- + +For build systems, being inside a chroot ensures that the build isn't +picking up files it shouldn't be. This helps avoid the problem of +"host contamination", where e.g. we want libfoo.h from inside our +root, not the one outside the root. Second, it helps avoid the fragility inherent in having to set up a large set of environment variables pointing to our root (e.g. PATH, @@ -17,13 +27,27 @@ the same as it normally is (/bin:/usr/bin). Security -------- +**** IMPORTANT NOTE **** + +Installing this tool accessible to all users significantly increases +their ability to perform local, authenticated denial of service +attacks. The intended mitigation against this is to ensure the tool +is only executable by certain users. + +**** IMPORTANT NOTE **** + The historical reason Unix doesn't allow chroot(2) as non-root is because of setuid binaries. It's trivial to use chroot to create a hostile environment, then execute a setuid binary to subvert it. This tool closes that historical hole by simply disallowing privilege gain by execution of setuid binaries. It creates a "nosuid" bind -mount over "/". +mount over "/". This restriction is typically irrelevant for build +systems. + +However, this tool also allows creating bind mounts, which currently +have no resource controls. This is why this tool is not intended to +be installed by default. Abilities granted ----------------- @@ -75,4 +99,3 @@ This binary can be installed in two modes: 1) uwsr-xr-x root:root - Executable by everyone 2) uwsr-x--- root:somegroup - Executable only by somegroup - -- cgit v1.2.1 From 8312b26c3733a04583084ee3bae9148db84c8b00 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 24 Apr 2012 08:56:26 -0400 Subject: Add --help and --version arguments --- src/linux-user-chroot.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/linux-user-chroot.c b/src/linux-user-chroot.c index 66285e5..7c2e678 100644 --- a/src/linux-user-chroot.c +++ b/src/linux-user-chroot.c @@ -24,6 +24,8 @@ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" + #define _GNU_SOURCE #include #include @@ -145,7 +147,17 @@ main (int argc, fatal ("Too many mounts (maximum of %u)", n_mounts); n_mounts++; - if (strcmp (arg, "--mount-bind") == 0) + if (strcmp (arg, "--help") == 0) + { + printf ("%s\n", "See \"man linux-user-chroot\""); + exit (0); + } + else if (strcmp (arg, "--version") == 0) + { + printf ("%s\n", PACKAGE_STRING); + exit (0); + } + else if (strcmp (arg, "--mount-bind") == 0) { if ((argc - after_mount_arg_index) < 3) fatal ("--mount-bind takes two arguments"); -- cgit v1.2.1 From 92457a2b2c29bba0ee272b7f86704ca10f44fd55 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 24 Apr 2012 09:05:52 -0400 Subject: Release 2012.1 --- Makefile.am | 3 +++ NEWS | 6 ++++++ configure.ac | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 NEWS diff --git a/Makefile.am b/Makefile.am index 741cdd0..e5279b2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -17,3 +17,6 @@ include Makefile-stub.am include Makefile-user-chroot.am + +release-tag: + git tag -m "Release $(VERSION)" v$(VERSION) diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..7e5921f --- /dev/null +++ b/NEWS @@ -0,0 +1,6 @@ +2012.1 +------ + +This is the first release with a new version numbering scheme of +. No important code changes, but we now include a +contributed manual page from Lars Wirzenius. diff --git a/configure.ac b/configure.ac index d2c6ded..ca3d860 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ([2.63]) -AC_INIT([linux-user-chroot], [3], [walters@verbum.org]) +AC_INIT([linux-user-chroot], [2012.1], [walters@verbum.org]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) -- cgit v1.2.1 From ce49cffb83d35e550c16b7aee23fac262e6f359d Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 10 Aug 2012 15:07:42 -0400 Subject: Make use of PR_SET_NO_NEW_PRIVS if available This flag is exactly what we want for this tool (it's what I thought SECBIT_NOROOT did). See the linked discussion from here: http://lwn.net/Articles/504879/ --- src/linux-user-chroot.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/linux-user-chroot.c b/src/linux-user-chroot.c index 7c2e678..3d5700c 100644 --- a/src/linux-user-chroot.c +++ b/src/linux-user-chroot.c @@ -5,7 +5,8 @@ * "safely": I believe that this program, when deployed as setuid on a * typical "distribution" such as RHEL or Debian, does not, even when * used in combination with typical software installed on that - * distribution, allow privilege escalation. + * distribution, allow privilege escalation. See the README for more + * details. * * Copyright 2011,2012 Colin Walters * @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +49,10 @@ #define SECBIT_NOROOT_LOCKED (1 << 1) #endif +#ifndef PR_SET_NO_NEW_PRIVS +#define PR_SET_NO_NEW_PRIVS 38 +#endif + static void fatal (const char *message, ...) __attribute__ ((noreturn)) __attribute__ ((format (printf, 1, 2))); static void fatal_errno (const char *message) __attribute__ ((noreturn)); @@ -274,16 +280,20 @@ main (int argc, if (child == 0) { /* - * SECBIT_NOROOT helps close the main historical reason why only - * uid 0 can chroot(2) - because unprivileged users can create - * hard links to setuid binaries, and possibly confuse them into - * looking at data (or loading libraries) that they don't - * expect, and thus elevating privileges. With this, executing - * a setuid program doesn't gain us any new Linux capabilities - * (but it still changes uid). See below for where we create a - * MS_NOSUID bind mount. + * First, we attempt to use PR_SET_NO_NEW_PRIVS, since it does + * exactly what we want - ensures the child can not gain any + * privileges, even attempting to execute setuid binaries. + * + * http://lwn.net/Articles/504879/ + * + * If that's not available, we fall back to using SECBIT_NOROOT. + * + * Following the belt-and-suspenders model, we also make a + * MS_NOSUID bind mount below. */ - if (prctl (PR_SET_SECUREBITS, + if (prctl (PR_SET_NO_NEW_PRIVS, 1) < 0 && errno != EINVAL) + fatal_errno ("prctl (PR_SET_NO_NEW_PRIVS)"); + else if (prctl (PR_SET_SECUREBITS, SECBIT_NOROOT | SECBIT_NOROOT_LOCKED) < 0) fatal_errno ("prctl (SECBIT_NOROOT)"); -- cgit v1.2.1 From 3af381d5ef6fa8b479589583db8c4efb199878e4 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 10 Aug 2012 15:35:09 -0400 Subject: Exit immediately if clone() fails This happens when run recursively. --- src/linux-user-chroot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linux-user-chroot.c b/src/linux-user-chroot.c index 3d5700c..217d651 100644 --- a/src/linux-user-chroot.c +++ b/src/linux-user-chroot.c @@ -275,7 +275,7 @@ main (int argc, clone_flags |= CLONE_NEWNET; if ((child = syscall (__NR_clone, clone_flags, NULL)) < 0) - perror ("clone"); + fatal_errno ("clone"); if (child == 0) { -- cgit v1.2.1 From 515c714471d0b5923f6633ef44a2270b23656ee9 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 10 Aug 2012 15:50:29 -0400 Subject: Release 2012.2 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ca3d860..7b0b546 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ([2.63]) -AC_INIT([linux-user-chroot], [2012.1], [walters@verbum.org]) +AC_INIT([linux-user-chroot], [2012.2], [walters@verbum.org]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) -- cgit v1.2.1 From 21a2e2b39af9f681d7ebeac72a6fcf0487a2b359 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 30 Dec 2012 04:58:40 -0500 Subject: Use MS_MOVE of / rather than chroot() chroot() breaks some tools that expect / to be an actual mount point. Doing namespace manipulation is cleaner than chroot(). See http://lists.freedesktop.org/archives/systemd-devel/2012-September/006703.html "[systemd-devel] OSTree mount integration" --- src/linux-user-chroot.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/linux-user-chroot.c b/src/linux-user-chroot.c index 217d651..82070b2 100644 --- a/src/linux-user-chroot.c +++ b/src/linux-user-chroot.c @@ -344,10 +344,19 @@ main (int argc, assert (0); free (dest); } - - /* Actually perform the chroot. */ - if (chroot (chroot_dir) < 0) + + if (chdir (chroot_dir) < 0) + fatal_errno ("chdir"); + + if (mount (chroot_dir, chroot_dir, NULL, MS_BIND | MS_PRIVATE, NULL) < 0) + fatal_errno ("mount (MS_BIND)"); + + if (mount (chroot_dir, "/", NULL, MS_MOVE, NULL) < 0) + fatal_errno ("mount (MS_MOVE)"); + + if (chroot (".") < 0) fatal_errno ("chroot"); + if (chdir (chdir_target) < 0) fatal_errno ("chdir"); -- cgit v1.2.1 From 61eea63b4042d1c6fba12d79215f5b9247d3b5c5 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Thu, 10 Jan 2013 14:25:37 -0500 Subject: Only MS_MOVE the root to / if the root isn't already / Otherwise the MS_MOVE call aborts. --- src/linux-user-chroot.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/linux-user-chroot.c b/src/linux-user-chroot.c index 82070b2..fa8a2be 100644 --- a/src/linux-user-chroot.c +++ b/src/linux-user-chroot.c @@ -351,11 +351,15 @@ main (int argc, if (mount (chroot_dir, chroot_dir, NULL, MS_BIND | MS_PRIVATE, NULL) < 0) fatal_errno ("mount (MS_BIND)"); - if (mount (chroot_dir, "/", NULL, MS_MOVE, NULL) < 0) - fatal_errno ("mount (MS_MOVE)"); + /* Only move if we're not actually just using / */ + if (strcmp (chroot_dir, "/") != 0) + { + if (mount (chroot_dir, "/", NULL, MS_MOVE, NULL) < 0) + fatal_errno ("mount (MS_MOVE)"); - if (chroot (".") < 0) - fatal_errno ("chroot"); + if (chroot (".") < 0) + fatal_errno ("chroot"); + } if (chdir (chdir_target) < 0) fatal_errno ("chdir"); -- cgit v1.2.1 From ef7cd3d4da4e72fe7b63174a3705d330177e5515 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 24 Feb 2013 05:48:02 -0500 Subject: build: use AC_SYS_LARGEFILE So we will do the right thing on 32 bit. --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index 7b0b546..c50f9b6 100644 --- a/configure.ac +++ b/configure.ac @@ -8,6 +8,8 @@ AM_INIT_AUTOMAKE([1.11 -Wno-portability foreign no-define tar-ustar no-dist-gzip AM_MAINTAINER_MODE([enable]) AM_SILENT_RULES([yes]) +AC_SYS_LARGEFILE + AC_PROG_CC AM_PROG_CC_C_O -- cgit v1.2.1 From c4388a624de392a72a5826b0d61c2aa21f283ede Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 24 Feb 2013 08:27:08 -0500 Subject: [SECURITY] Invoke chdir() after we've switched uid, not before Otherwise, the user can access otherwise inaccessible directories like: $ linux-user-chroot --chdir /root/.virsh / /bin/sh Reported-by: Ryan Lortie Reported-by: Marc Deslauriers --- src/linux-user-chroot.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/linux-user-chroot.c b/src/linux-user-chroot.c index fa8a2be..ac542ad 100644 --- a/src/linux-user-chroot.c +++ b/src/linux-user-chroot.c @@ -361,9 +361,6 @@ main (int argc, fatal_errno ("chroot"); } - if (chdir (chdir_target) < 0) - fatal_errno ("chdir"); - /* Switch back to the uid of our invoking process. These calls are * irrevocable - see setuid(2) */ if (setgid (rgid) < 0) @@ -371,6 +368,9 @@ main (int argc, if (setuid (ruid) < 0) fatal_errno ("setuid"); + if (chdir (chdir_target) < 0) + fatal_errno ("chdir"); + if (execvp (program, program_argv) < 0) fatal_errno ("execv"); } -- cgit v1.2.1 From 04028db1d9f7909f4c86d9b4d4a8640e65fd11f1 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 24 Feb 2013 08:33:31 -0500 Subject: [SECURITY] Use fsuid to lookup bind mount paths and chroot target Otherise, the user can access otherwise inaccessible directories like this: $ linux-user-chroot --mount-bind /root/.virsh ~/mnt / /bin/sh Also, we should check the accessibility of the chroot target; this is much harder to exploit because you'd need an executable inside the chroot that can be run. Reported-by: Marc Deslauriers Reported-by: Ryan Lortie Reviewed-by: Marc Deslauriers Signed-off-by: Colin Walters --- src/linux-user-chroot.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/linux-user-chroot.c b/src/linux-user-chroot.c index ac542ad..6cac578 100644 --- a/src/linux-user-chroot.c +++ b/src/linux-user-chroot.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -110,6 +111,28 @@ reverse_mount_list (MountSpec *mount) return prev; } +/** + * fsuid_chdir: + * @uid: User id we should use + * @path: Path string + * + * Like chdir() except we use the filesystem privileges of @uid. + */ +static int +fsuid_chdir (uid_t uid, + const char *path) +{ + int errsv; + int ret; + /* Note we don't check errors here because we can't, basically */ + (void) setfsuid (uid); + ret = chdir (path); + errsv = errno; + (void) setfsuid (0); + errno = errsv; + return ret; +} + int main (int argc, char **argv) @@ -330,7 +353,9 @@ main (int argc, } else if (bind_mount_iter->type == MOUNT_SPEC_BIND) { - if (mount (bind_mount_iter->source, dest, + if (fsuid_chdir (ruid, bind_mount_iter->source) < 0) + fatal ("Couldn't chdir to bind mount source"); + if (mount (".", dest, NULL, MS_BIND | MS_PRIVATE, NULL) < 0) fatal_errno ("mount (MS_BIND)"); } @@ -345,10 +370,10 @@ main (int argc, free (dest); } - if (chdir (chroot_dir) < 0) + if (fsuid_chdir (ruid, chroot_dir) < 0) fatal_errno ("chdir"); - if (mount (chroot_dir, chroot_dir, NULL, MS_BIND | MS_PRIVATE, NULL) < 0) + if (mount (".", ".", NULL, MS_BIND | MS_PRIVATE, NULL) < 0) fatal_errno ("mount (MS_BIND)"); /* Only move if we're not actually just using / */ -- cgit v1.2.1 From 9d4ac4e39c64eaf23f90c28fc97ac426d809c33d Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 24 Feb 2013 10:56:21 -0500 Subject: Release 2013.1 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c50f9b6..4910b32 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ([2.63]) -AC_INIT([linux-user-chroot], [2012.2], [walters@verbum.org]) +AC_INIT([linux-user-chroot], [2013.1], [walters@verbum.org]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) -- cgit v1.2.1 From 5d23708442b16138b800a4e4e9daf20eda50ba46 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 23 Sep 2013 17:06:05 -0400 Subject: Bump up bind mount limit to 1024 The Baserock people were hitting up against the limit of 50, which as the newly added comment says isn't really effective against DoS anyways, so let's just bump it up significantly. Tested-by: Lars Wirzenius --- src/linux-user-chroot.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/linux-user-chroot.c b/src/linux-user-chroot.c index 6cac578..8b8700d 100644 --- a/src/linux-user-chroot.c +++ b/src/linux-user-chroot.c @@ -54,6 +54,16 @@ #define PR_SET_NO_NEW_PRIVS 38 #endif +/* Totally arbitrary; we're just trying to mitigate somewhat against + * DoS attacks. In practice uids can typically spawn multiple + * processes, so this isn't effective. What is needed is for the + * kernel to understand we're creating bind mounts on behalf of a + * given uid. Most likely this will happen if the kernel obsoletes + * this tool by allowing processes with PR_SET_NO_NEW_PRIVS to create + * private mounts or chroot. + */ +#define MAX_BIND_MOUNTS 1024 + static void fatal (const char *message, ...) __attribute__ ((noreturn)) __attribute__ ((format (printf, 1, 2))); static void fatal_errno (const char *message) __attribute__ ((noreturn)); @@ -145,7 +155,7 @@ main (int argc, gid_t rgid, egid, sgid; int after_mount_arg_index; unsigned int n_mounts = 0; - const unsigned int max_mounts = 50; /* Totally arbitrary... */ + const unsigned int max_mounts = MAX_BIND_MOUNTS; char **program_argv; MountSpec *bind_mounts = NULL; MountSpec *bind_mount_iter; -- cgit v1.2.1