initial commit

This commit is contained in:
Alessandro Ghedini 2013-12-05 22:20:00 +01:00
commit 420789dbbb
20 changed files with 1789 additions and 0 deletions

47
CMakeLists.txt Normal file
View file

@ -0,0 +1,47 @@
# pflask CMakeLists.txt
# Copyright (C) 2013 Alessandro Ghedini <alessandro@ghedini.me>
# 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
)

23
COPYING Normal file
View file

@ -0,0 +1,23 @@
Copyright (c) 2013, Alessandro Ghedini <alessandro@ghedini.me>
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 <COPYRIGHT HOLDER> 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.

87
README.md Normal file
View file

@ -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 <alessandro@ghedini.me>
See COPYING for the license.

66
src/dev.c Normal file
View file

@ -0,0 +1,66 @@
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/stat.h>
#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()");
}
}

3
src/dev.h Normal file
View file

@ -0,0 +1,3 @@
extern void make_ptmx(char *dest);
extern void make_console(char *dest, char *console);
extern void copy_nodes(char *dest);

198
src/mount.c Normal file
View file

@ -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 <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/stat.h>
#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;
}
}

37
src/mount.h Normal file
View file

@ -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);

136
src/netif.c Normal file
View file

@ -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 <netlink/msg.h>
#include <netlink/attr.h>
#include <netlink/errno.h>
#include <netlink/socket.h>
#include <netlink/route/link.h>
#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);
}

34
src/netif.h Normal file
View file

@ -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);

296
src/pflask.c Normal file
View file

@ -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 <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sched.h>
#include <linux/sched.h>
#include <signal.h>
#include <getopt.h>
#include <sys/stat.h>
#include <sys/wait.h>
#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("");
}

101
src/printf.c Normal file
View file

@ -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 <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#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);
}

41
src/printf.h Normal file
View file

@ -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, ...);

343
src/pty.c Normal file
View file

@ -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 <stdlib.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/signalfd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#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()");
}
}

37
src/pty.h Normal file
View file

@ -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);

98
src/user.c Normal file
View file

@ -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 <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#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)");
}

33
src/user.h Normal file
View file

@ -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);

58
src/util.c Normal file
View file

@ -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 <stdlib.h>
#include <string.h>
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;
}

42
src/util.h Normal file
View file

@ -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 <stdlib.h>
#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);

31
src/version.h.in Normal file
View file

@ -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}"

78
tools/pflask-debuild Executable file
View file

@ -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