From 420789dbbb2963e0c450ea5e3428d0a74dbcf444 Mon Sep 17 00:00:00 2001 From: Alessandro Ghedini Date: Thu, 5 Dec 2013 22:20:00 +0100 Subject: [PATCH] initial commit --- CMakeLists.txt | 47 ++++++ COPYING | 23 +++ README.md | 87 +++++++++++ src/dev.c | 66 +++++++++ src/dev.h | 3 + src/mount.c | 198 +++++++++++++++++++++++++ src/mount.h | 37 +++++ src/netif.c | 136 +++++++++++++++++ src/netif.h | 34 +++++ src/pflask.c | 296 +++++++++++++++++++++++++++++++++++++ src/printf.c | 101 +++++++++++++ src/printf.h | 41 ++++++ src/pty.c | 343 +++++++++++++++++++++++++++++++++++++++++++ src/pty.h | 37 +++++ src/user.c | 98 +++++++++++++ src/user.h | 33 +++++ src/util.c | 58 ++++++++ src/util.h | 42 ++++++ src/version.h.in | 31 ++++ tools/pflask-debuild | 78 ++++++++++ 20 files changed, 1789 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 COPYING create mode 100644 README.md create mode 100644 src/dev.c create mode 100644 src/dev.h create mode 100644 src/mount.c create mode 100644 src/mount.h create mode 100644 src/netif.c create mode 100644 src/netif.h create mode 100644 src/pflask.c create mode 100644 src/printf.c create mode 100644 src/printf.h create mode 100644 src/pty.c create mode 100644 src/pty.h create mode 100644 src/user.c create mode 100644 src/user.h create mode 100644 src/util.c create mode 100644 src/util.h create mode 100644 src/version.h.in create mode 100755 tools/pflask-debuild diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..331a0c8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,47 @@ +# pflask CMakeLists.txt +# Copyright (C) 2013 Alessandro Ghedini +# This file is released under the 2 clause BSD license, see COPYING + +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +PROJECT(pflask C) + +SET(VERSION_MAJOR 0) +SET(VERSION_MINOR 1) + +SET(PFLASK_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}) + +SET(INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) + +INCLUDE(FindPkgConfig) + +PKG_CHECK_MODULES(NL libnl-3.0 REQUIRED) +PKG_CHECK_MODULES(NL_ROUTE libnl-route-3.0 REQUIRED) + +FILE(GLOB SOURCES RELATIVE ${CMAKE_SOURCE_DIR} "src/*.c") + +ADD_EXECUTABLE(pflask ${SOURCES}) + +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in + ${CMAKE_CURRENT_BINARY_DIR}/version.h) + +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${NL_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR} +) + +TARGET_LINK_LIBRARIES(pflask + ${NL_LIBRARIES} + ${NL_ROUTE_LIBRARIES} +) + +SET_TARGET_PROPERTIES(pflask PROPERTIES + COMPILE_FLAGS "-Wall -pedantic -g -std=gnu99 -D_GNU_SOURCE" +) + +INSTALL(TARGETS pflask DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + +INSTALL(FILES + ${PROJECT_SOURCE_DIR}/docs/pflask.1 + DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1 +) diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..2a5397f --- /dev/null +++ b/COPYING @@ -0,0 +1,23 @@ +Copyright (c) 2013, Alessandro Ghedini +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..690a50b --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +pflask +====== + +![Travis CI](https://secure.travis-ci.org/ghedo/pflask.png) + +**pflask** is a simple tool for creating Linux namespace containers. It can be +used for running a command or even booting an OS inside an isolated container, +created with the help of Linux namespaces. It is similar in functionality to +`chroot(8)`, altough pflask provides better isolation thanks to the use of +namespaces. + +Compared to [LXC] [LXC], pflask is easier to use since it doesn't require any +pre-configuration (all the options can be passed via the command-line), but LXC +is better suited for production, or generally security-sensitive environments. + +Compared to [systemd-nspawn] [systemd], pflask doesn't require the use of +systemd on the host system and provides additional features such as a more +comprehensive handling of mounts and network interfaces inside the container. + +[LXC]: http://linuxcontainers.org +[systemd]: http://www.freedesktop.org/software/systemd/man/systemd-nspawn.html + +## FEATURES + +### User namespace + +When the host system allows it, pflask creates a new user namespace inside the +container, and automatically maps the user running pflask to the root user +inside the container. This means that a user could create and have full root +privileges inside a container, while having none on the host system. + +Note that this has been the cause of security vulnerabilities in the past, so +that most OS vendors (reasonably) decided to either disable user namespace +support altogether, or restrict the functionality to root. + +pflask can disable the relevant functionality when it detects that support for +user namespaces is not available. + +### Mount namespace + +By default, pflask creates a new mount namespace inside the container, so that +filesystems mounted inside it won't affect the host system. pflask can also be +told to create new mount points before the execution of the supplied command, +by using the `--mount` option. Supported mount point types are: + + * `bind` -- bind mount a directory/file to another directory/file + * `aufs` -- stack a directory on top of another directory using AuFS + * `loop` -- mount a loop device (TODO) + * `tmp` -- mount a tmpfs on a directory + +### Network namespace + +When supplied the `--netif` option, pflask will create a new network namespace +and move/rename the supplied network interface inside the container. + +### PID, IPC and UTS namespaces + +By default, pflask creates new PID, IPC and UTS namespaces inside the container, +in order to isolate processes, IPC resources and the node/domain name of the +container from the host system. + +## GETTING STARTED + +See the [man page](http://ghedo.github.io/pflask/) for more information. + +## DEPENDENCIES + + * `linux` + * `libnl` + * `libnl-route` + +## BUILDING + +pflask is distributed as source code. Install with: + +```bash +$ mkdir build && cd build +$ cmake .. +$ make +$ [sudo] make install +``` + +## COPYRIGHT + +Copyright (C) 2013 Alessandro Ghedini + +See COPYING for the license. diff --git a/src/dev.c b/src/dev.c new file mode 100644 index 0000000..fb84418 --- /dev/null +++ b/src/dev.c @@ -0,0 +1,66 @@ +#include +#include +#include + +#include +#include + +#include "printf.h" +#include "util.h" + +void make_ptmx(char *dest) { + int rc; + + _free_ char *ptmx_dst = NULL; + + rc = asprintf(&ptmx_dst, "%s/dev/ptmx", dest); + if (rc < 0) fail_printf("OOM"); + + rc = symlink("/dev/pts/ptmx", ptmx_dst); + if (rc < 0) sysf_printf("symlink()"); +} + +void make_console(char *dest, char *console) { + int rc; + struct stat sb; + _free_ char *target = NULL; + + rc = stat(console, &sb); + if (rc < 0) sysf_printf("stat()"); + + rc = asprintf(&target, "%s/dev/console", dest); + if (rc < 0) fail_printf("OOM"); + + rc = mknod(target, (sb.st_mode & ~07777) | 0600, sb.st_rdev); + if (rc < 0) sysf_printf("mknod()"); + + rc = mount(console, target, NULL, MS_BIND, NULL); + if (rc < 0) sysf_printf("mount()"); +} + +void copy_nodes(char *dest) { + int i; + int rc; + + const char *nodes[] = { + "/dev/tty", + "/dev/null", + "/dev/zero", + "/dev/random", + "/dev/urandom" + }; + + for (i = 0; i < sizeof(nodes) / sizeof(*nodes); i++) { + struct stat sb; + _free_ char *target = NULL; + + rc = asprintf(&target, "%s%s", dest, nodes[i]); + if (rc < 0) fail_printf("OOM"); + + rc = stat(nodes[i], &sb); + if (rc < 0) sysf_printf("stat()"); + + rc = mknod(target, sb.st_mode, sb.st_rdev); + if (rc < 0) sysf_printf("mknod()"); + } +} diff --git a/src/dev.h b/src/dev.h new file mode 100644 index 0000000..f5dd2ff --- /dev/null +++ b/src/dev.h @@ -0,0 +1,3 @@ +extern void make_ptmx(char *dest); +extern void make_console(char *dest, char *console); +extern void copy_nodes(char *dest); diff --git a/src/mount.c b/src/mount.c new file mode 100644 index 0000000..4cbc8c0 --- /dev/null +++ b/src/mount.c @@ -0,0 +1,198 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "printf.h" +#include "util.h" + +typedef struct MOUNT_LIST { + char *src; + char *dst; + char *type; + unsigned long flags; + void *data; + + struct MOUNT_LIST *next; +} mount_list; + +static mount_list *mounts = NULL; + +void add_mount(char *src, char *dst, char *type, unsigned long f, void *d) { + mount_list *mnt = malloc(sizeof(mount_list)); + if (mnt == NULL) fail_printf("OOM"); + + mnt -> src = src ? strdup(src) : NULL; + mnt -> dst = dst ? strdup(dst) : NULL; + mnt -> type = type ? strdup(type) : NULL; + mnt -> flags = f; + mnt -> data = d ? strdup(d) : NULL; + + mnt -> next = NULL; + + if (mounts) + mnt -> next = mounts; + + mounts = mnt; +} + +void add_mount_inside(char *base, char *src, char *dst, char *type, + unsigned long f, void *d) { + int rc; + + _free_ char *target = NULL; + + rc = asprintf(&target, "%s%s", base, dst); + if (rc < 0) fail_printf("OOM"); + + add_mount(src, target, type, f, d); +} + +void add_mount_from_spec(char *spec) { + int rc; + _free_ char **opts = NULL; + + _free_ char *tmp = strdup(spec); + if (tmp == NULL) fail_printf("OOM"); + + size_t c = split_str(tmp, &opts, ","); + if (c == 0) fail_printf("Invalid mount spec '%s'", spec); + + if (strncmp(opts[0], "bind", 5) == 0) { + char *src, *dst; + + if (c < 3) fail_printf("Invalid mount spec '%s'", spec); + + src = realpath(opts[1], NULL); + if (src == NULL) sysf_printf("realpath()"); + + dst = realpath(opts[2], NULL); + if (dst == NULL) sysf_printf("realpath()"); + + add_mount(src, dst, NULL, MS_BIND, NULL); + } else if (strncmp(opts[0], "aufs", 4) == 0) { + char *dst, *overlay; + _free_ char *aufs_opts = NULL; + + if (c < 3) fail_printf("Invalid mount spec '%s'", spec); + + overlay = realpath(opts[1], NULL); + if (overlay == NULL) sysf_printf("realpath()"); + + dst = realpath(opts[2], NULL); + if (dst == NULL) sysf_printf("realpath()"); + + rc = asprintf(&aufs_opts, "br:%s=rw:%s=ro", overlay, dst); + if (rc < 0) fail_printf("OOM"); + + add_mount(NULL, dst, "aufs", 0, aufs_opts); + } else if (strncmp(opts[0], "loop", 5) == 0) { + /* TODO: loop device support */ + } else if (strncmp(opts[0], "tmp", 4) == 0) { + char *dst; + + if (c < 2) fail_printf("Invalid mount spec '%s'", spec); + + dst = realpath(opts[1], NULL); + if (dst == NULL) sysf_printf("realpath()"); + + add_mount("tmpfs", dst, "tmpfs", 0, NULL); + } else { + fail_printf("Invalid mount type '%s'", opts[0]); + } +} + +void do_mount(char *dest) { + int rc; + + mount_list *i = NULL; + + rc = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL); + if (rc < 0) sysf_printf("mount(MS_SLAVE)"); + + if (dest != NULL) { + add_mount(dest, dest, NULL, MS_BIND, NULL); + + add_mount_inside(dest, "proc", "/proc", "proc", + MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL); + + add_mount_inside(dest, "/proc/sys", "/proc/sys", NULL, + MS_BIND, NULL); + + add_mount_inside(dest, NULL, "/proc/sys", NULL, + MS_BIND | MS_RDONLY | MS_REMOUNT, NULL); + + add_mount_inside(dest, "sysfs", "/sys", "sysfs", + MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RDONLY, NULL); + + add_mount_inside(dest, "tmpfs", "/dev", "tmpfs", + MS_NOSUID | MS_STRICTATIME, "mode=755"); + + add_mount_inside(dest, "devpts", "/dev/pts", "devpts", + MS_NOSUID | MS_NOEXEC, + "newinstance,ptmxmode=000,mode=620,gid=5"); + + add_mount_inside(dest, "tmpfs", "/dev/shm", "tmpfs", + MS_NOSUID | MS_STRICTATIME | MS_NODEV, "mode=1777"); + + add_mount_inside(dest, "tmpfs", "/run", "tmpfs", + MS_NOSUID | MS_NODEV | MS_STRICTATIME, "mode=755"); + + /* add_mount(dest, "/", NULL, MS_MOVE, NULL); */ + } + + while (mounts) { + mount_list *next = mounts -> next; + mounts -> next = i; + i = mounts; + mounts = next; + } + + while (i != NULL) { + struct stat sb; + + if (stat(i -> dst, &sb) < 0) { + rc = mkdir(i -> dst, 0755); + if (rc < 0) sysf_printf("mkdir(%s)", i -> dst); + } + + rc = mount(i->src, i -> dst, i -> type, i -> flags, i -> data); + if (rc < 0) sysf_printf("mount(%s)", i -> type); + + i = i -> next; + } +} diff --git a/src/mount.h b/src/mount.h new file mode 100644 index 0000000..fac7180 --- /dev/null +++ b/src/mount.h @@ -0,0 +1,37 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +extern void add_mount(char *src, char *dst, char *type, + unsigned long f, void *d); +extern void add_mount_inside(char *base, char *src, char *dst, char *type, + unsigned long f, void *d); +extern void add_mount_from_spec(char *spec); + +extern void do_mount(char *dest); diff --git a/src/netif.c b/src/netif.c new file mode 100644 index 0000000..e8c2194 --- /dev/null +++ b/src/netif.c @@ -0,0 +1,136 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include "netif.h" +#include "printf.h" +#include "util.h" + +typedef struct NETIF_LIST { + char *dev; + char *name; + + struct NETIF_LIST *next; +} netif_list; + +static netif_list *netifs = NULL; + +void add_netif(char *dev, char *name) { + netif_list *nif = malloc(sizeof(netif_list)); + if (nif == NULL) fail_printf("OOM"); + + nif -> dev = strdup(dev); + nif -> name = strdup(name); + + nif -> next = NULL; + + if (netifs) + nif -> next = netifs; + + netifs = nif; +} + +void add_netif_from_spec(char *spec) { + _free_ char **opts = NULL; + + _free_ char *tmp = strdup(spec); + if (tmp == NULL) fail_printf("OOM"); + + size_t c = split_str(tmp, &opts, ","); + if (c == 0) fail_printf("Invalid netif spec '%s'", spec); + + if (c < 2) fail_printf("Invalid netif spec '%s'", spec); + + add_netif(opts[0], opts[1]); +} + +void do_netif(pid_t pid) { + int rc; + netif_list *i = NULL; + + struct nl_sock *sock; + struct nl_cache *cache; + + sock = nl_socket_alloc(); + if (sock == NULL) fail_printf("OOM"); + + rc = nl_connect(sock, NETLINK_ROUTE); + if (rc < 0) fail_printf("Error creating netlink connection: %s", + nl_geterror(rc)); + + rc = rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache); + if (rc < 0) fail_printf("Error creating netlink cache: %s", + nl_geterror(rc)); + + while (netifs) { + netif_list *next = netifs -> next; + netifs -> next = i; + i = netifs; + netifs = next; + } + + while (i != NULL) { + struct nl_msg *msg; + struct rtnl_link *new_link; + + struct rtnl_link *link = rtnl_link_get_by_name(cache, i->dev); + if (link == NULL) fail_printf("Invalid netif '%s'", i -> dev); + + new_link = rtnl_link_alloc(); + if (new_link == NULL) fail_printf("OOM"); + + rtnl_link_set_name(new_link, i -> name); + + rc = rtnl_link_build_change_request(link, new_link, 0, &msg); + if (rc < 0) fail_printf("Error creating netlink request: '%s'", + nl_geterror(rc)); + + nla_put_u32(msg, IFLA_NET_NS_PID, pid); + + rc = nl_send_auto(sock, msg); + if (rc < 0) fail_printf("Error sending netlink request: %s", + nl_geterror(rc)); + + i = i -> next; + + rtnl_link_put(new_link); + rtnl_link_put(link); + } + + nl_cache_put(cache); + + nl_close(sock); + nl_socket_free(sock); +} diff --git a/src/netif.h b/src/netif.h new file mode 100644 index 0000000..301c0b7 --- /dev/null +++ b/src/netif.h @@ -0,0 +1,34 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +extern void add_netif(char *dev, char *name); +extern void add_netif_from_spec(char *spec); + +extern void do_netif(pid_t pid); diff --git a/src/pflask.c b/src/pflask.c new file mode 100644 index 0000000..b90709a --- /dev/null +++ b/src/pflask.c @@ -0,0 +1,296 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +#include "dev.h" +#include "pty.h" +#include "user.h" +#include "mount.h" +#include "netif.h" +#include "printf.h" +#include "util.h" +#include "version.h" + +static int clone_flags = SIGCHLD | + CLONE_NEWNS | + CLONE_NEWIPC | + CLONE_NEWPID | + /* CLONE_NEWUSER| */ + CLONE_NEWUTS; + +static inline void help(void); +static inline void version(void); + +void do_chroot(char *dest) { + int rc; + + rc = chdir(dest); + if (rc < 0) sysf_printf("chdir()"); + + rc = chroot("."); + if (rc < 0) sysf_printf("chroot()"); + + rc = chdir("/"); + if (rc < 0) sysf_printf("chdir(/)"); +} + +pid_t do_clone(void) { + pid_t pid; + + pid = syscall(__NR_clone, clone_flags, NULL); + if (pid < 0) { + if (errno == EINVAL) { + clone_flags &= ~(CLONE_NEWUSER); + pid = syscall(__NR_clone, clone_flags, NULL); + } + } + + if (pid < 0) sysf_printf("clone()"); + + return pid; +} + +static struct option long_opts[] = { + { "mount", required_argument, NULL, 'm' }, + { "netif", required_argument, NULL, 'n' }, + { "user", required_argument, NULL, 'u' }, + { "root", required_argument, NULL, 'r' }, + { "chdir", required_argument, NULL, 'c' }, + { "help", no_argument, NULL, 'h' }, + { 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) { + int rc, i; + + pid_t pid; + uid_t uid; + gid_t gid; + + _free_ char *user = NULL; + _free_ char *dest = NULL; + _free_ char *change = NULL; + + int master_fd; + char *master_name; + + siginfo_t status; + + const char *short_opts = "+m:n:u:r:h"; + + if (argc < 2) { + help(); + return 0; + } + + while ((rc = getopt_long(argc, argv, short_opts, long_opts, &i)) !=-1) { + switch (rc) { + case 'm': + add_mount_from_spec(optarg); + break; + + case 'n': + clone_flags |= CLONE_NEWNET; + add_netif_from_spec(optarg); + break; + + case 'u': + user = strdup(optarg); + break; + + case 'r': + dest = realpath(optarg, NULL); + if (dest == NULL) sysf_printf("realpath()"); + break; + + case 'c': + change = strdup(optarg); + break; + + case '?': + case 'h': + help(); + return 0; + } + } + + if (user == NULL) { + user = strdup("root"); + if (user == NULL) fail_printf("OOM"); + } + + open_master_pty(&master_fd, &master_name); + + uid = getuid(); + gid = getgid(); + + pid = do_clone(); + + if (pid == 0) { + _free_ char *env_term = NULL; + _free_ char *env_user = NULL; + _free_ char *env_name = NULL; + + char **env = NULL; + + rc = asprintf(&env_user, "USER=%s", user); + if (rc < 0) fail_printf("OOM"); + + rc = asprintf(&env_name, "LOGNAME=%s", user); + if (rc < 0) fail_printf("OOM"); + + if (getenv("TERM")) { + rc = asprintf(&env_term, "TERM=%s", getenv("TERM")); + if (rc < 0) fail_printf("OOM"); + } + + const char *envp[] = { + "container=pflask", + "PATH=/usr/sbin:/usr/bin:/sbin:/bin", + env_user, + env_name, + env_term, + NULL + }; + + rc = setsid(); + if (rc < 0) sysf_printf("setsid()"); + + open_slave_pty(master_name); + + rc = close(master_fd); + if (rc < 0) sysf_printf("close()"); + + if (clone_flags & CLONE_NEWUSER) + map_user_to_root(uid, gid); + + do_mount(dest); + + if (dest != NULL) { + make_ptmx(dest); + make_console(dest, master_name); + + copy_nodes(dest); + + do_chroot(dest); + } + + umask(0022); + + /* TODO: drop capabilities */ + + do_user(user); + + if (change) { + rc = chdir(change); + if (rc < 0) sysf_printf("chdir()"); + } + + env = dest == NULL ? environ : envp; + + if (argc > optind) + rc = execvpe(argv[optind], argv + optind, env); + else + rc = execle("/bin/bash", "-bash", NULL, env); + + if (rc < 0) sysf_printf("exec()"); + } + + do_netif(pid); + + /* TODO: attach/detach */ + process_pty(master_fd); + + rc = waitid(P_PID, pid, &status, WEXITED); + if (rc < 0) sysf_printf("waitid()"); + + switch (status.si_code) { + case CLD_EXITED: + if (status.si_status != 0) + err_printf("Child failed with code '%d'", + status.si_status); + else + ok_printf("Child exited"); + break; + + case CLD_KILLED: + err_printf("Child was terminated"); + break; + + default: + err_printf("Child failed"); + break; + } + + return 0; +} + +static inline void version(void) { + printf("pflask v%s\n", PFLASK_VERSION); +} + +static inline void help(void) { + #define CMD_HELP(CMDL, CMDS, MSG) printf(" %s, %s \t%s.\n", COLOR_YELLOW CMDS, CMDL COLOR_OFF, MSG); + + printf(COLOR_RED "Usage: " COLOR_OFF); + printf(COLOR_GREEN "pflask " COLOR_OFF); + puts("[OPTIONS] [COMMAND [ARGS...]]\n"); + + puts(COLOR_RED " Options:" COLOR_OFF); + + CMD_HELP("--mount", "-m", + "Create a new mount point inside the container"); + CMD_HELP("--netif", "-n", + "Move a network interface inside the container"); + CMD_HELP("--user", "-u", + "Run the command as the specified user"); + CMD_HELP("--root", "-r", + "Use the specified directory as root inside the container"); + CMD_HELP("--chdir", "-c", + "Change to the specified directory inside the namespace"); + + puts(""); +} diff --git a/src/printf.c b/src/printf.c new file mode 100644 index 0000000..e05476c --- /dev/null +++ b/src/printf.c @@ -0,0 +1,101 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "printf.h" + +void ok_printf(const char *fmt, ...) { + va_list args; + + /* if (cfg.silent || cfg.quiet) */ + /* return; */ + + va_start(args, fmt); + fprintf(stderr, "[" COLOR_GREEN "✔" COLOR_OFF "] "); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +} + +void debug_printf(const char *fmt, ...) { + va_list args; + + /* if (!cfg.verbose || cfg.silent || cfg.quiet) */ + /* return; */ + + va_start(args, fmt); + fprintf(stderr, "[" COLOR_YELLOW "¡" COLOR_OFF "] "); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +} + +void err_printf(const char *fmt, ...) { + va_list args; + + /* if (cfg.silent) */ + /* return; */ + + va_start(args, fmt); + fprintf(stderr, "[" COLOR_RED "✘" COLOR_OFF "] "); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +} + +void fail_printf(const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + fprintf(stderr, "[" COLOR_RED "✘" COLOR_OFF "] "); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + + _exit(-1); +} + +void sysf_printf(const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + fprintf(stderr, "[" COLOR_RED "✘" COLOR_OFF "] "); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, ": %s\n", strerror(errno)); + + exit(-1); +} diff --git a/src/printf.h b/src/printf.h new file mode 100644 index 0000000..7c77e3b --- /dev/null +++ b/src/printf.h @@ -0,0 +1,41 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define COLOR_GREEN "" +#define COLOR_YELLOW "" +#define COLOR_RED "" +#define COLOR_BGRED "" +#define COLOR_OFF "" + +extern void ok_printf(const char *fmt, ...); +extern void debug_printf(const char *fmt, ...); +extern void err_printf(const char *fmt, ...); +extern void fail_printf(const char *fmt, ...); +extern void sysf_printf(const char *fmt, ...); diff --git a/src/pty.c b/src/pty.c new file mode 100644 index 0000000..c9b6cbd --- /dev/null +++ b/src/pty.c @@ -0,0 +1,343 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "printf.h" + +#define SOCKET_PATH "/tmp/fuck-security" + +static struct termios stdin_attr; +static struct winsize stdin_ws; + +static void recv_fd(int sock); +static void send_fd(int sock, int fd); + +void open_master_pty(int *master_fd, char **master_name) { + int rc; + + rc = tcgetattr(STDIN_FILENO, &stdin_attr); + if (rc < 0) sysf_printf("tcgetattr()"); + + rc = ioctl(STDIN_FILENO, TIOCGWINSZ, &stdin_ws); + if (rc < 0) sysf_printf("ioctl(TIOCGWINSZ)"); + + *master_fd = posix_openpt(O_RDWR | O_NOCTTY | O_NDELAY); + if (*master_fd < 0) sysf_printf("posix_openpt()"); + + *master_name = ptsname(*master_fd); + if (*master_name == NULL) sysf_printf("ptsname()"); + + rc = unlockpt(*master_fd); + if (rc < 0) sysf_printf("unlckpt()"); +} + +void open_slave_pty(char *master_name) { + int rc, slave_fd; + + slave_fd = open(master_name, O_RDWR, 0); + if (slave_fd < 0) sysf_printf("open()"); + + if (!isatty(slave_fd)) fail_printf("Not a TTY"); + + rc = dup2(slave_fd, STDIN_FILENO); + if (rc < 0) sysf_printf("dup2(STDIN)"); + + rc = dup2(slave_fd, STDOUT_FILENO); + if (rc < 0) sysf_printf("dup2(STDOUT)"); + + rc = dup2(slave_fd, STDERR_FILENO); + if (rc < 0) sysf_printf("dup2(STDERR)"); + + rc = tcsetattr(slave_fd, TCSANOW, &stdin_attr); + if (rc < 0) sysf_printf("tcsetattr()"); + + rc = ioctl(slave_fd, TIOCSWINSZ, &stdin_ws); + if (rc < 0) sysf_printf("ioctl(TIOCWINSZ)"); + + rc = close(slave_fd); + if (rc < 0) sysf_printf("close()"); +} + +void process_pty(int master_fd) { + int rc; + + fd_set rfds; + + sigset_t mask; + int signal_fd; + + struct termios raw_attr; + + int line_max = sysconf(_SC_LINE_MAX); + if (line_max < 0) sysf_printf("sysconf()"); + + memcpy(&raw_attr, &stdin_attr, sizeof(stdin_attr)); + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGCHLD); + sigaddset(&mask, SIGWINCH); + + rc = sigprocmask(SIG_BLOCK, &mask, NULL); + if (rc < 0) sysf_printf("sigprocmask()"); + + signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); + if (signal_fd < 0) sysf_printf("signalfd()"); + + cfmakeraw(&raw_attr); + raw_attr.c_lflag &= ~ECHO; + + rc = tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr); + if (rc < 0) sysf_printf("tcsetattr()"); + + FD_ZERO(&rfds); + + FD_SET(STDIN_FILENO, &rfds); + FD_SET(master_fd, &rfds); + FD_SET(signal_fd, &rfds); + + while ((rc = select(master_fd + 1, &rfds, NULL, NULL, NULL)) > 0) { + char buf[line_max]; + + if (FD_ISSET(STDIN_FILENO, &rfds)) { + int rc = read(STDIN_FILENO, buf, line_max); + + if (rc == 0) + goto finish; + else if (rc < 0) + goto finish; + + rc = write(master_fd, buf, rc); + if (rc < 0) sysf_printf("write()"); + } + + if (FD_ISSET(master_fd, &rfds)) { + rc = read(master_fd, buf, line_max); + + if (rc == 0) + goto finish; + else if (rc < 0) + goto finish; + + rc = write(STDOUT_FILENO, buf, rc); + if (rc < 0) sysf_printf("write()"); + } + + if (FD_ISSET(signal_fd, &rfds)) { + struct signalfd_siginfo fdsi; + + rc = read(signal_fd, &fdsi, sizeof(fdsi)); + if (rc != sizeof(fdsi)) sysf_printf("read()"); + + switch (fdsi.ssi_signo) { + case SIGWINCH: { + struct winsize ws; + + rc = ioctl(STDIN_FILENO, TIOCGWINSZ, &ws); + if (rc < 0) sysf_printf("ioctl()"); + + rc = ioctl(master_fd, TIOCSWINSZ, &ws); + if (rc < 0) sysf_printf("ioctl()"); + } + + case SIGCHLD: + goto finish; + } + } + + FD_SET(STDIN_FILENO, &rfds); + FD_SET(master_fd, &rfds); + FD_SET(signal_fd, &rfds); + } + if (rc < 0) sysf_printf("select()"); + +finish: + rc = tcsetattr(STDIN_FILENO, TCSANOW, &stdin_attr); + if (rc < 0) sysf_printf("tcsetattr()"); +} + +void serve_pty(int fd) { + int rc; + int sock; + + struct sockaddr *servaddr; + struct sockaddr_un servaddr_un; + int servaddr_len; + + memset(&servaddr_un, 0, sizeof(struct sockaddr_un)); + + servaddr_un.sun_family = AF_UNIX; + strcpy(servaddr_un.sun_path, SOCKET_PATH); + + servaddr = (struct sockaddr *) &servaddr_un; + servaddr_len = sizeof(struct sockaddr_un) - + sizeof(servaddr_un.sun_path) + + strlen(SOCKET_PATH); + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) sysf_printf("socket()"); + + rc = bind(sock, servaddr, servaddr_len); + if (rc < 0) sysf_printf("bind()"); + + rc = listen(sock, 1); + if (rc < 0) sysf_printf("listen()"); + + while (1) { + int send_sock = accept(sock, (struct sockaddr *) NULL, NULL); + if (send_sock < 0) sysf_printf("accept()"); + + send_fd(send_sock, fd); + } +} + +void recv_pty(void) { + int rc; + int sock; + + struct sockaddr *servaddr; + struct sockaddr_un servaddr_un; + int servaddr_len; + + memset(&servaddr_un, 0, sizeof(struct sockaddr_un)); + + servaddr_un.sun_family = AF_UNIX; + strcpy(servaddr_un.sun_path, SOCKET_PATH); + + servaddr = (struct sockaddr *) &servaddr_un; + servaddr_len = sizeof(struct sockaddr_un) - + sizeof(servaddr_un.sun_path) + + strlen(SOCKET_PATH); + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) sysf_printf("socket()"); + + rc = connect(sock, servaddr, servaddr_len); + if (rc < 0) sysf_printf("connect()"); + + recv_fd(sock); +} + +static void send_fd(int sock, int fd) { + int rc; + + union { + struct cmsghdr cmsg; + char control[CMSG_SPACE(sizeof(int))]; + } msg_control; + + struct msghdr msg; + struct iovec iov[1]; + struct cmsghdr *cmsg; + + iov [0].iov_base = "x"; + iov [0].iov_len = 1; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_flags = 0; + + msg.msg_control = NULL; + msg.msg_controllen = 0; + + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof(msg_control); + + cmsg = CMSG_FIRSTHDR(&msg); + + cmsg -> cmsg_len = CMSG_LEN(sizeof(int)); + cmsg -> cmsg_level = SOL_SOCKET; + cmsg -> cmsg_type = SCM_RIGHTS; + + *((int *) CMSG_DATA(cmsg)) = fd; + + rc = sendmsg(sock, &msg, 0); + if (rc < 0) sysf_printf("sendmsg()"); +} + +static void recv_fd(int sock) { + int rc; + + union { + struct cmsghdr cmsg; + char control [CMSG_SPACE(sizeof(int))]; + } msg_control; + + struct msghdr msg; + struct iovec iov[1]; + struct cmsghdr *cmsg; + char buf[192]; + + iov [0].iov_base = buf; + iov [0].iov_len = sizeof(buf); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof(msg_control); + msg.msg_flags = 0; + + rc = recvmsg(sock, &msg, 0); + if (rc < 0) sysf_printf("recvmsg()"); + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + int fd; + + if (cmsg -> cmsg_len != CMSG_LEN(sizeof(int)) || + cmsg -> cmsg_level != SOL_SOCKET || + cmsg -> cmsg_type != SCM_RIGHTS) + continue; + + fd = *((int *) CMSG_DATA(cmsg)); + process_pty(fd); + + rc = close(fd); + if (rc < 0) sysf_printf("close()"); + } +} diff --git a/src/pty.h b/src/pty.h new file mode 100644 index 0000000..409c651 --- /dev/null +++ b/src/pty.h @@ -0,0 +1,37 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +extern void open_master_pty(int *master_fd, char **master_name); +extern void open_slave_pty(char *master_name); + +extern void process_pty(int master_fd); + +extern void serve_pty(int fd); +extern void recv_pty(void); diff --git a/src/user.c b/src/user.c new file mode 100644 index 0000000..e59f4ec --- /dev/null +++ b/src/user.c @@ -0,0 +1,98 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "printf.h" +#include "util.h" + +void map_user_to_root(uid_t uid, gid_t gid) { + int rc; + int map_fd; + + _free_ char *uid_map = NULL; + _free_ char *gid_map = NULL; + + rc = asprintf(&uid_map, "0 %d 1", uid); + if (rc < 0) fail_printf("OOM"); + + map_fd = open("/proc/self/uid_map", O_RDWR); + if (map_fd < 0) sysf_printf("open(uid_map)"); + + rc = write(map_fd, uid_map, strlen(uid_map)); + if (rc < 0) sysf_printf("write(uid_map)"); + + rc = close(map_fd); + if (rc < 0) sysf_printf("close(uid_map)"); + + rc = asprintf(&gid_map, "0 %d 1", gid); + if (rc < 0) fail_printf("OOM"); + + map_fd = open("/proc/self/gid_map", O_RDWR); + if (map_fd < 0) sysf_printf("open(gid_map)"); + + rc = write(map_fd, gid_map, strlen(gid_map)); + if (rc < 0) sysf_printf("write(gid_map)"); + + rc = close(map_fd); + if (rc < 0) sysf_printf("close(gid_map)"); +} + +void do_user(char *user) { + int rc; + + struct passwd *pwd; + + errno = 0; + pwd = getpwnam(user); + if (pwd == NULL) { + if (errno) sysf_printf("getpwnam()"); + else fail_printf("Invalid user '%s'", user); + } + + rc = initgroups(user, pwd -> pw_gid); + if (rc < 0) sysf_printf("initgroups()"); + + rc = setresgid(pwd -> pw_gid, pwd -> pw_gid, pwd -> pw_gid); + if (rc < 0) sysf_printf("setresgid()"); + + rc = setresuid(pwd -> pw_uid, pwd -> pw_uid, pwd -> pw_uid); + if (rc < 0) sysf_printf("setresuid()"); + + rc = chdir(pwd -> pw_dir); + if (rc < 0) sysf_printf("chdir(HOME)"); +} diff --git a/src/user.h b/src/user.h new file mode 100644 index 0000000..cded38e --- /dev/null +++ b/src/user.h @@ -0,0 +1,33 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +extern void map_user_to_root(uid_t uid, gid_t gid); + +extern void do_user(char *user); diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..1f04d9c --- /dev/null +++ b/src/util.c @@ -0,0 +1,58 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +size_t split_str(char *orig, char ***dest, char *needle) { + size_t size = 0; + char *token; + + if (orig == NULL || dest == NULL) + return 0; + + token = strtok(orig, needle); + + do { + char **tmp = realloc(*dest, sizeof(char *) * (size + 1)); + + if (tmp == NULL) { + if (*dest != NULL) + free(*dest); + + return 0; + } + + *dest = tmp; + (*dest)[size++] = token; + } while ((token = strtok(NULL, needle)) != NULL); + + return size; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..39edce0 --- /dev/null +++ b/src/util.h @@ -0,0 +1,42 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#define _free_ __attribute__((cleanup(freep))) + +static inline void freep(void *p) { + if (p == NULL) + return; + + free(*(void **) p); +} + +extern size_t split_str(char *orig, char ***dest, char *needle); diff --git a/src/version.h.in b/src/version.h.in new file mode 100644 index 0000000..dec1bb0 --- /dev/null +++ b/src/version.h.in @@ -0,0 +1,31 @@ +/* + * The process in the flask. + * + * Copyright (c) 2013, Alessandro Ghedini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#cmakedefine PFLASK_VERSION "${PFLASK_VERSION}" diff --git a/tools/pflask-debuild b/tools/pflask-debuild new file mode 100755 index 0000000..7e31922 --- /dev/null +++ b/tools/pflask-debuild @@ -0,0 +1,78 @@ +#!/bin/bash +# +# Build Debian packages inside Linux namespaces. +# +# Copyright (c) 2013, Alessandro Ghedini +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set -e + +umask 022 + +DIST=unstable +ARCH=amd64 + +PFLASK="$HOME/devel/projs/pflask/build/pflask" + +BASE_DIR="$HOME/local/base-$DIST-$ARCH" + +PKG_DIR=$(pwd) + +PKG=$(basename $PKG_DIR) +DIR=$(dirname $PKG_DIR) + +OVERLAY="$DIR/$PKG-XXXX" + +OVERLAY=$(mktemp -d $OVERLAY) + +USER=$(whoami) + +APT='apt-get --no-install-recommends -y' + +$PFLASK --user $USER --root $BASE_DIR \ + --mount "aufs,$OVERLAY,$BASE_DIR" \ + --mount "bind,$DIR,$BASE_DIR/tmp" \ + --chdir "/tmp/$PKG" \ + -- \ + mk-build-deps -r -i debian/control -t $APT + +$PFLASK --user $USER --root $BASE_DIR \ + --mount "aufs,$OVERLAY,$BASE_DIR" \ + --mount "bind,$DIR,$BASE_DIR/tmp" \ + --chdir "/tmp/$PKG" \ + -- \ + debuild + +$PFLASK --user $USER --root $BASE_DIR \ + --mount "aufs,$OVERLAY,$BASE_DIR" \ + --mount "bind,$DIR,$BASE_DIR/tmp" \ + --chdir "/tmp/$PKG" \ + -- \ + debclean + +rm -rf $OVERLAY + +exit 0