[ostree] ostbuild: Allow binding arbitrary directories, don't hardcode /proc /dev
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ostree] ostbuild: Allow binding arbitrary directories, don't hardcode /proc /dev
- Date: Tue, 6 Dec 2011 19:07:45 +0000 (UTC)
commit 304272469896e63594fbeb20dbbb80d31c9f7f84
Author: Colin Walters <walters verbum org>
Date: Tue Dec 6 14:06:45 2011 -0500
ostbuild: Allow binding arbitrary directories, don't hardcode /proc /dev
This is just more flexible, and eventually we want this to be a
generic user-chroot tool.
src/ostbuild/ostbuild-user-chroot.c | 119 +++++++++++++++++++++++++---------
1 files changed, 87 insertions(+), 32 deletions(-)
---
diff --git a/src/ostbuild/ostbuild-user-chroot.c b/src/ostbuild/ostbuild-user-chroot.c
index c0ed1a2..ed8d616 100644
--- a/src/ostbuild/ostbuild-user-chroot.c
+++ b/src/ostbuild/ostbuild-user-chroot.c
@@ -25,6 +25,9 @@
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/prctl.h>
@@ -32,50 +35,71 @@
#include <linux/securebits.h>
#include <sched.h>
-typedef unsigned int bool;
+static void fatal (const char *message, ...) __attribute__ ((noreturn)) __attribute__ ((format (printf, 1, 2)));
+static void fatal_errno (const char *message) __attribute__ ((noreturn));
static void
-fatal_errno (const char *message) __attribute__ ((noreturn));
-
-static void
-fatal_errno (const char *message)
+fatal (const char *fmt,
+ ...)
{
- perror (message);
+ va_list args;
+
+ va_start (args, fmt);
+
+ vfprintf (stderr, fmt, args);
+ putc ('\n', stderr);
+
+ va_end (args);
exit (1);
}
static void
-initialize_chroot (const char *path)
+fatal_errno (const char *message)
{
- char *subpath;
-
- asprintf (&subpath, "%s/proc", path);
- if (mount ("/proc", subpath, NULL, MS_BIND, NULL) < 0)
- fatal_errno ("bind mounting proc");
- free (subpath);
-
- asprintf (&subpath, "%s/dev", path);
- if (mount ("/dev", subpath, NULL, MS_BIND, NULL) < 0)
- fatal_errno ("bind mounting dev");
- free (subpath);
+ perror (message);
+ exit (1);
}
int
main (int argc,
char **argv)
{
+ const char *argv0;
const char *chroot_dir;
const char *program;
uid_t ruid, euid, suid;
gid_t rgid, egid, sgid;
+ int after_bind_arg_index;
+ int i;
+ char **program_argv;
+ char **argv_iter;
+
+ if (argc <= 0)
+ return 1;
+
+ argv0 = argv[0];
+ argc--;
+ argv++;
- if (argc < 3)
+ if (argc < 1)
+ fatal ("ROOTDIR argument must be specified");
+
+ after_bind_arg_index = 0;
+ argv_iter = argv;
+ while (after_bind_arg_index < argc
+ && strcmp (argv[after_bind_arg_index], "--bind") == 0)
{
- fprintf (stderr, "usage: %s DIR PROGRAM ARGS...\n", argv[0]);
- exit (1);
+ if ((argc - after_bind_arg_index) < 3)
+ fatal ("--bind takes two arguments");
+ after_bind_arg_index += 3;
+ argv_iter += 3;
}
- chroot_dir = argv[1];
- program = argv[2];
+
+ if ((argc - after_bind_arg_index) < 2)
+ fatal ("usage: %s [--bind SOURCE DEST] ROOTDIR PROGRAM ARGS...", argv0);
+ chroot_dir = argv[after_bind_arg_index];
+ program = argv[after_bind_arg_index+1];
+ program_argv = argv + after_bind_arg_index + 1;
if (getresgid (&rgid, &egid, &sgid) < 0)
fatal_errno ("getresgid");
@@ -83,38 +107,69 @@ main (int argc,
fatal_errno ("getresuid");
if (ruid == 0)
- {
- fprintf (stderr, "error: ruid is 0\n");
- exit (1);
- }
+ fatal ("error: ruid is 0");
if (rgid == 0)
rgid = ruid;
- /* Ensure we can't execute setuid programs - see prctl(2) and capabilities(7) */
+ /* Ensure we can't execute setuid programs. See prctl(2) and
+ * capabilities(7).
+ *
+ * This closes 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.
+ */
if (prctl (PR_SET_SECUREBITS,
SECBIT_NOROOT | SECBIT_NOROOT_LOCKED) < 0)
- fatal_errno ("prctl");
+ fatal_errno ("prctl (SECBIT_NOROOT)");
+ /* This call makes it so that when we create bind mounts, we're only
+ * affecting our children, not the entire system. This way it's
+ * harmless to bind mount e.g. /proc over an arbitrary directory.
+ */
if (unshare (CLONE_NEWNS) < 0)
fatal_errno ("unshare (CLONE_NEWNS)");
+ /* This is necessary to undo the damage "sandbox" creates on Fedora
+ * by making / a shared mount instead of private. This isn't
+ * totally correct because the targets for our bind mounts may still
+ * be shared, but really, Fedora's sandbox is broken.
+ */
if (mount ("/", "/", "none", MS_PRIVATE, NULL) < 0)
fatal_errno ("mount(/, MS_PRIVATE)");
- initialize_chroot (chroot_dir);
+ /* Now let's set up our bind mounts */
+ for (i = 0; i < after_bind_arg_index; i += 3)
+ {
+ const char *bind_arg = argv[0]; /* --bind */
+ const char *bind_source = argv[i+1];
+ const char *bind_target = argv[i+2];
+ char *bind_abs_target;
+
+ assert (strcmp (bind_arg, "--bind") == 0);
+
+ asprintf (&bind_abs_target, "%s%s", chroot_dir, bind_target);
+ if (mount (bind_source, bind_abs_target, NULL, MS_BIND | MS_PRIVATE, NULL) < 0)
+ fatal_errno ("mount (MS_BIND)");
+ free (bind_abs_target);
+ }
+ /* Actually perform the chroot. */
if (chroot (chroot_dir) < 0)
fatal_errno ("chroot");
if (chdir ("/") < 0)
fatal_errno ("chdir");
- /* These are irrevocable - see setuid(2) */
+ /* Switch back to the uid of our invoking process. These calls are
+ * irrevocable - see setuid(2) */
if (setgid (rgid) < 0)
fatal_errno ("setgid");
if (setuid (ruid) < 0)
fatal_errno ("setuid");
- if (execv (program, argv + 2) < 0)
+ /* Finally, run the given child program. */
+ if (execv (program, program_argv) < 0)
fatal_errno ("execv");
return 1;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]