diff --git a/Makefile-libglnx.am b/Makefile-libglnx.am index f2c654914f9bcea220ff94927778082406d39236..87754188cdb411b5ee3d4c022d599d2eeb26df16 100644 --- a/Makefile-libglnx.am +++ b/Makefile-libglnx.am @@ -30,6 +30,8 @@ libglnx_la_SOURCES = \ $(libglnx_srcpath)/glnx-dirfd.c \ $(libglnx_srcpath)/glnx-fdio.h \ $(libglnx_srcpath)/glnx-fdio.c \ + $(libglnx_srcpath)/glnx-libcontainer.h \ + $(libglnx_srcpath)/glnx-libcontainer.c \ $(libglnx_srcpath)/glnx-xattrs.h \ $(libglnx_srcpath)/glnx-xattrs.c \ $(libglnx_srcpath)/glnx-shutil.h \ diff --git a/glnx-libcontainer.c b/glnx-libcontainer.c new file mode 100644 index 0000000000000000000000000000000000000000..dafd8ad4073317683c7f8b5d374d429043e82fb1 --- /dev/null +++ b/glnx-libcontainer.c @@ -0,0 +1,249 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Portions derived from src/nspawn/nspawn.c: + * Copyright 2010 Lennart Poettering + * + * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <string.h> +#include <glib-unix.h> +#include <sys/mount.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <stdio.h> +#include <gio/gunixoutputstream.h> + +#include "glnx-libcontainer.h" + +#include "glnx-backport-autocleanups.h" +#include "glnx-local-alloc.h" + +static gboolean container_available = TRUE; + +static void _perror_fatal (const char *message) __attribute__ ((noreturn)); + +static void +_perror_fatal (const char *message) +{ + perror (message); + exit (1); +} + +void +glnx_libcontainer_set_not_available (void) +{ + container_available = FALSE; +} + +gboolean +glnx_libcontainer_get_available (void) +{ + return container_available; +} + +gboolean +glnx_libcontainer_bind_mount_readonly (const char *path, GError **error) +{ + gboolean ret = FALSE; + + if (mount (path, path, NULL, MS_BIND | MS_PRIVATE, NULL) != 0) + { + int errsv = errno; + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "mount(%s, MS_BIND): %s", + path, + g_strerror (errsv)); + goto out; + } + if (mount (path, path, NULL, MS_BIND | MS_PRIVATE | MS_REMOUNT | MS_RDONLY, NULL) != 0) + { + int errsv = errno; + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "mount(%s, MS_BIND | MS_RDONLY): %s", + path, + g_strerror (errsv)); + goto out; + } + + ret = TRUE; + out: + return ret; +} + + +/* Based on code from nspawn.c */ +int +glnx_libcontainer_make_api_mounts (const char *dest) +{ + typedef struct MountPoint { + const char *what; + const char *where; + const char *type; + const char *options; + unsigned long flags; + gboolean fatal; + } MountPoint; + + static const MountPoint mount_table[] = { + { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, TRUE }, + { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, TRUE }, /* Bind mount first */ + { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, TRUE }, /* Then, make it r/o */ + { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, TRUE }, + { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, TRUE }, + { "devpts", "/dev/pts", "devpts","newinstance,ptmxmode=0666,mode=620,gid=5", MS_NOSUID|MS_NOEXEC, TRUE }, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, TRUE }, + { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, TRUE }, + { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, FALSE }, /* Bind mount first */ + { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, FALSE }, /* Then, make it r/o */ + }; + + unsigned k; + + for (k = 0; k < G_N_ELEMENTS(mount_table); k++) + { + g_autofree char *where = NULL; + int t; + + where = g_build_filename (dest, mount_table[k].where, NULL); + + t = mkdir (where, 0755); + if (t < 0 && errno != EEXIST) + { + if (!mount_table[k].fatal) + continue; + return -1; + } + + if (mount (mount_table[k].what, + where, + mount_table[k].type, + mount_table[k].flags, + mount_table[k].options) < 0) + { + if (errno == ENOENT && !mount_table[k].fatal) + continue; + return -1; + } + } + + return 0; +} + +int +glnx_libcontainer_prep_dev (const char *dest_devdir) +{ + glnx_fd_close int src_fd = -1; + glnx_fd_close int dest_fd = -1; + struct stat stbuf; + guint i; + static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" }; + + src_fd = openat (AT_FDCWD, "/dev", O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); + if (src_fd == -1) + return -1; + + dest_fd = openat (AT_FDCWD, dest_devdir, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY); + if (dest_fd == -1) + return -1; + + for (i = 0; i < G_N_ELEMENTS (devnodes); i++) + { + const char *nodename = devnodes[i]; + + if (fstatat (src_fd, nodename, &stbuf, 0) == -1) + { + if (errno == ENOENT) + continue; + else + return -1; + } + + if (mknodat (dest_fd, nodename, stbuf.st_mode, stbuf.st_rdev) != 0) + return -1; + if (fchmodat (dest_fd, nodename, stbuf.st_mode, 0) != 0) + return -1; + } + + return 0; +} + +pid_t +glnx_libcontainer_run_in_root (const char *dest, + const char *binary, + char **argv) +{ + const int cloneflags = + SIGCHLD | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_SYSVSEM | CLONE_NEWUTS; + pid_t child; + + if (container_available) + { + if ((child = syscall (__NR_clone, cloneflags, NULL)) < 0) + return -1; + } + else + { + if ((child = fork ()) < 0) + return -1; + } + + if (child != 0) + return child; + + if (container_available) + { + if (mount (NULL, "/", "none", MS_PRIVATE | MS_REC, NULL) != 0) + _perror_fatal ("mount: "); + + if (mount (NULL, "/", "none", MS_PRIVATE | MS_REMOUNT | MS_NOSUID, NULL) != 0) + _perror_fatal ("mount (MS_NOSUID): "); + } + + if (chdir (dest) != 0) + _perror_fatal ("chdir: "); + + if (container_available) + { + if (glnx_libcontainer_make_api_mounts (dest) != 0) + _perror_fatal ("preparing api mounts: "); + + if (glnx_libcontainer_prep_dev ("dev") != 0) + _perror_fatal ("preparing /dev: "); + + if (mount (".", ".", NULL, MS_BIND | MS_PRIVATE, NULL) != 0) + _perror_fatal ("mount (MS_BIND)"); + + if (mount (dest, "/", NULL, MS_MOVE, NULL) != 0) + _perror_fatal ("mount (MS_MOVE)"); + } + + if (chroot (".") != 0) + _perror_fatal ("chroot: "); + + if (chdir ("/") != 0) + _perror_fatal ("chdir: "); + + if (execv (binary, argv) != 0) + _perror_fatal ("execl: "); + + g_assert_not_reached (); +} diff --git a/glnx-libcontainer.h b/glnx-libcontainer.h new file mode 100644 index 0000000000000000000000000000000000000000..16cdb654a2dc19c09ff98459b6befb89176886f0 --- /dev/null +++ b/glnx-libcontainer.h @@ -0,0 +1,44 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2014,2015 Colin Walters <walters@verbum.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include <gio/gio.h> +#include <sched.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/prctl.h> +#include <sys/fsuid.h> +#include <sys/syscall.h> +#include <sys/wait.h> +#include <sys/capability.h> +#include <sched.h> + +void glnx_libcontainer_set_not_available (void); +gboolean glnx_libcontainer_get_available (void); + +gboolean glnx_libcontainer_bind_mount_readonly (const char *path, GError **error); + +int glnx_libcontainer_make_api_mounts (const char *dest); +int glnx_libcontainer_prep_dev (const char *dest); + +pid_t glnx_libcontainer_run_in_root (const char *dest, + const char *binary, + char **argv); diff --git a/libglnx.h b/libglnx.h index 3e815851c88c0853bf46b09829ce9097b2a6f659..89defac1a605b0cb108014f69757cb0a1981d322 100644 --- a/libglnx.h +++ b/libglnx.h @@ -30,6 +30,7 @@ G_BEGIN_DECLS #include <glnx-dirfd.h> #include <glnx-shutil.h> #include <glnx-xattrs.h> +#include <glnx-libcontainer.h> #include <glnx-console.h> #include <glnx-fdio.h>