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.
This commit is contained in:
parent
0fb40b201f
commit
3042724698
|
|
@ -25,6 +25,9 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
|
|
@ -32,10 +35,23 @@
|
||||||
#include <linux/securebits.h>
|
#include <linux/securebits.h>
|
||||||
#include <sched.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
|
static void
|
||||||
fatal_errno (const char *message) __attribute__ ((noreturn));
|
fatal (const char *fmt,
|
||||||
|
...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start (args, fmt);
|
||||||
|
|
||||||
|
vfprintf (stderr, fmt, args);
|
||||||
|
putc ('\n', stderr);
|
||||||
|
|
||||||
|
va_end (args);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fatal_errno (const char *message)
|
fatal_errno (const char *message)
|
||||||
|
|
@ -44,38 +60,46 @@ fatal_errno (const char *message)
|
||||||
exit (1);
|
exit (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
initialize_chroot (const char *path)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main (int argc,
|
main (int argc,
|
||||||
char **argv)
|
char **argv)
|
||||||
{
|
{
|
||||||
|
const char *argv0;
|
||||||
const char *chroot_dir;
|
const char *chroot_dir;
|
||||||
const char *program;
|
const char *program;
|
||||||
uid_t ruid, euid, suid;
|
uid_t ruid, euid, suid;
|
||||||
gid_t rgid, egid, sgid;
|
gid_t rgid, egid, sgid;
|
||||||
|
int after_bind_arg_index;
|
||||||
|
int i;
|
||||||
|
char **program_argv;
|
||||||
|
char **argv_iter;
|
||||||
|
|
||||||
if (argc < 3)
|
if (argc <= 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
argv0 = argv[0];
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
|
||||||
|
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]);
|
if ((argc - after_bind_arg_index) < 3)
|
||||||
exit (1);
|
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)
|
if (getresgid (&rgid, &egid, &sgid) < 0)
|
||||||
fatal_errno ("getresgid");
|
fatal_errno ("getresgid");
|
||||||
|
|
@ -83,38 +107,69 @@ main (int argc,
|
||||||
fatal_errno ("getresuid");
|
fatal_errno ("getresuid");
|
||||||
|
|
||||||
if (ruid == 0)
|
if (ruid == 0)
|
||||||
{
|
fatal ("error: ruid is 0");
|
||||||
fprintf (stderr, "error: ruid is 0\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
if (rgid == 0)
|
if (rgid == 0)
|
||||||
rgid = ruid;
|
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,
|
if (prctl (PR_SET_SECUREBITS,
|
||||||
SECBIT_NOROOT | SECBIT_NOROOT_LOCKED) < 0)
|
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)
|
if (unshare (CLONE_NEWNS) < 0)
|
||||||
fatal_errno ("unshare (CLONE_NEWNS)");
|
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)
|
if (mount ("/", "/", "none", MS_PRIVATE, NULL) < 0)
|
||||||
fatal_errno ("mount(/, MS_PRIVATE)");
|
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)
|
if (chroot (chroot_dir) < 0)
|
||||||
fatal_errno ("chroot");
|
fatal_errno ("chroot");
|
||||||
if (chdir ("/") < 0)
|
if (chdir ("/") < 0)
|
||||||
fatal_errno ("chdir");
|
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)
|
if (setgid (rgid) < 0)
|
||||||
fatal_errno ("setgid");
|
fatal_errno ("setgid");
|
||||||
if (setuid (ruid) < 0)
|
if (setuid (ruid) < 0)
|
||||||
fatal_errno ("setuid");
|
fatal_errno ("setuid");
|
||||||
|
|
||||||
if (execv (program, argv + 2) < 0)
|
/* Finally, run the given child program. */
|
||||||
|
if (execv (program, program_argv) < 0)
|
||||||
fatal_errno ("execv");
|
fatal_errno ("execv");
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue