Use just a single list for mount-points (RO, RW, chroot)
authorJagger <robert@swiecki.net>
Sat, 17 Oct 2015 14:48:30 +0000 (16:48 +0200)
committerJagger <robert@swiecki.net>
Sat, 17 Oct 2015 14:48:30 +0000 (16:48 +0200)
cmdline.c
common.h
contain.c

index 3969400786cca43195d17c89852a520d27dd6149..6f1eac27187e39ab265bbdcc45dc7a86ef37f913 100644 (file)
--- a/cmdline.c
+++ b/cmdline.c
 #include <stdbool.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <string.h>
 #include <strings.h>
 #include <sys/personality.h>
+#include <sys/mount.h>
 #include <sys/time.h>
 #include <unistd.h>
 
@@ -99,15 +101,10 @@ void cmdlineLogParams(struct nsjconf_t *nsjconf)
             logYesNo(nsjconf->clone_newuts), logYesNo(nsjconf->apply_sandbox),
             logYesNo(nsjconf->keep_caps), nsjconf->tmpfs_size);
 
-       struct constchar_t *p;
-       LIST_FOREACH(p, &nsjconf->robindmountpts, pointers) {
-               LOG_I("Additional (ro) bind mount point: '%s'", p->value);
-       }
-       LIST_FOREACH(p, &nsjconf->rwbindmountpts, pointers) {
-               LOG_I("Additional (rw) bind mount point: '%s'", p->value);
-       }
-       LIST_FOREACH(p, &nsjconf->tmpfsmountpts, pointers) {
-               LOG_I("Additional tmpfs mount point: '%s'", p->value);
+       struct mounts_t *p;
+       LIST_FOREACH(p, &nsjconf->mountpts, pointers) {
+               LOG_I("Mount point: src:'%s' dst:'%s' type:'%s' flags:%tx options:'%s'",
+                     p->src, p->dst, p->fs_type, p->flags, p->options);
        }
 }
 
@@ -150,12 +147,34 @@ rlim_t cmdlineParseRLimit(int res, const char *optarg, unsigned long mul)
        return val;
 }
 
+/* findSpecDestination mutates spec (source:dest) to have a null byte instead
+ * of ':' in between source and dest, then returns a pointer to the dest
+ * string. */
+static char *cmdlineMountParam(char *spec)
+{
+       char *dest = spec;
+       while (*dest != ':' && *dest != '\0') {
+               dest++;
+       }
+
+       switch (*dest) {
+       case ':':
+               *dest = '\0';
+               return dest + 1;
+       case '\0':
+               return spec;
+       default:
+               // not reached
+               return spec;
+       }
+}
+
 bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
 {
        /*  *INDENT-OFF* */
        (*nsjconf) = (struct nsjconf_t) {
                .hostname = "NSJAIL",
-               .chroot = "/chroot",
+               .chroot = "",
                .argv = NULL,
                .port = 31337,
                .uid = -1,
@@ -192,13 +211,12 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
        /*  *INDENT-OFF* */
 
        LIST_INIT(&nsjconf->pids);
-       LIST_INIT(&nsjconf->robindmountpts);
-       LIST_INIT(&nsjconf->rwbindmountpts);
-       LIST_INIT(&nsjconf->tmpfsmountpts);
+       LIST_INIT(&nsjconf->mountpts);
 
        const char *user = "nobody";
        const char *group = "nobody";
        const char *logfile = NULL;
+       static char cmdlineTmpfsSz[PATH_MAX] = "size=4194304";
 
         /*  *INDENT-OFF* */
        struct custom_option custom_opts[] = {
@@ -208,7 +226,7 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                        "\to: Immediately launch a single process on a console using clone/execve [MODE_STANDALONE_ONCE]\n"
                        "\te: Immediately launch a single process on a console using execve [MODE_STANDALONE_EXECVE]\n"
                        "\tr: Immediately launch a single process on a console, keep doing it forever [MODE_STANDALONE_RERUN]"},
-               {{"chroot", required_argument, NULL, 'c'}, "Directory containing / of the jail (default: '/chroot')"},
+               {{"chroot", required_argument, NULL, 'c'}, "Directory containing / of the jail (default: none)"},
                {{"user", required_argument, NULL, 'u'}, "Username/uid of processess inside the jail (default: 'nobody')"},
                {{"group", required_argument, NULL, 'g'}, "Groupname/gid of processess inside the jail (default: 'nogroup')"},
                {{"hostname", required_argument, NULL, 'H'}, "UTS name (hostname) of the jail (default: 'NSJAIL')"},
@@ -369,38 +387,52 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                        break;
                case 0x0602:
                        nsjconf->tmpfs_size = strtoull(optarg, NULL, 0);
+                       snprintf(cmdlineTmpfsSz, sizeof(cmdlineTmpfsSz), "size=%zu",
+                                nsjconf->tmpfs_size);
                        break;
                case 0x0603:
                        nsjconf->mount_proc = false;
                        break;
                case 'R':
                        {
-                               struct constchar_t *p = malloc(sizeof(struct constchar_t));
+                               struct mounts_t *p = malloc(sizeof(struct mounts_t));
                                if (p == NULL) {
-                                       PLOG_F("malloc(%zu)", sizeof(struct constchar_t));
+                                       PLOG_F("malloc(%zu)", sizeof(struct mounts_t));
                                }
-                               p->value = optarg;
-                               LIST_INSERT_HEAD(&nsjconf->robindmountpts, p, pointers);
+                               p->src = optarg;
+                               p->dst = cmdlineMountParam(optarg);
+                               p->flags = MS_BIND | MS_REC | MS_PRIVATE | MS_RDONLY;
+                               p->options = NULL;
+                               p->fs_type = NULL;
+                               LIST_INSERT_HEAD(&nsjconf->mountpts, p, pointers);
                        }
                        break;
                case 'B':
                        {
-                               struct constchar_t *p = malloc(sizeof(struct constchar_t));
+                               struct mounts_t *p = malloc(sizeof(struct mounts_t));
                                if (p == NULL) {
-                                       PLOG_F("malloc(%zu)", sizeof(struct constchar_t));
+                                       PLOG_F("malloc(%zu)", sizeof(struct mounts_t));
                                }
-                               p->value = optarg;
-                               LIST_INSERT_HEAD(&nsjconf->rwbindmountpts, p, pointers);
+                               p->src = optarg;
+                               p->dst = cmdlineMountParam(optarg);
+                               p->flags = MS_BIND | MS_REC | MS_PRIVATE;
+                               p->options = NULL;
+                               p->fs_type = NULL;
+                               LIST_INSERT_HEAD(&nsjconf->mountpts, p, pointers);
                        }
                        break;
                case 'T':
                        {
-                               struct constchar_t *p = malloc(sizeof(struct constchar_t));
+                               struct mounts_t *p = malloc(sizeof(struct mounts_t));
                                if (p == NULL) {
-                                       PLOG_F("malloc(%zu)", sizeof(struct constchar_t));
+                                       PLOG_F("malloc(%zu)", sizeof(struct mounts_t));
                                }
-                               p->value = optarg;
-                               LIST_INSERT_HEAD(&nsjconf->tmpfsmountpts, p, pointers);
+                               p->src = "none";
+                               p->dst = optarg;
+                               p->flags = 0;
+                               p->options = cmdlineTmpfsSz;
+                               p->fs_type = "tmpfs";
+                               LIST_INSERT_HEAD(&nsjconf->mountpts, p, pointers);
                        }
                        break;
                case 'M':
@@ -436,6 +468,34 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                }
        }
 
+       if (nsjconf->mount_proc == true) {
+               struct mounts_t *p = malloc(sizeof(struct mounts_t));
+               if (p == NULL) {
+                       PLOG_F("malloc(%zu)", sizeof(struct mounts_t));
+               }
+               p->src = "none";
+               p->dst = "/proc";
+               p->flags = 0;
+               p->options = NULL;
+               p->fs_type = "proc";
+               LIST_INSERT_HEAD(&nsjconf->mountpts, p, pointers);
+       }
+       if (strlen(nsjconf->chroot) > 0) {
+               struct mounts_t *p = malloc(sizeof(struct mounts_t));
+               if (p == NULL) {
+                       PLOG_F(" malloc(%zu) ", sizeof(struct mounts_t));
+               }
+               p->src = nsjconf->chroot;
+               p->dst = "/";
+               p->flags = MS_BIND | MS_REC | MS_PRIVATE;
+               p->options = NULL;
+               p->fs_type = NULL;
+               if (nsjconf->is_root_rw == false) {
+                       p->flags |= MS_RDONLY;
+               }
+               LIST_INSERT_HEAD(&nsjconf->mountpts, p, pointers);
+       }
+
        if (logInitLogFile(nsjconf, logfile, nsjconf->verbose) == false) {
                return false;
        }
index 57102060f6249a49142751c2a858ca75e98efeda..0b34db046e019a529392bf7b895279f5889aa68d 100644 (file)
--- a/common.h
+++ b/common.h
@@ -22,6 +22,7 @@
 #ifndef _COMMON_H
 #define _COMMON_H
 
+#include <limits.h>
 #include <netinet/ip6.h>
 #include <stdbool.h>
 #include <sys/queue.h>
@@ -38,9 +39,13 @@ struct pids_t {
         LIST_ENTRY(pids_t) pointers;
 };
 
-struct constchar_t {
-       const char *value;
-        LIST_ENTRY(constchar_t) pointers;
+struct mounts_t {
+       const char *src;
+       const char *dst;
+       const char *fs_type;
+       const char *options;
+       uintptr_t flags;
+        LIST_ENTRY(mounts_t) pointers;
 };
 
 enum mode_t {
@@ -52,7 +57,6 @@ enum mode_t {
 
 struct nsjconf_t {
        const char *hostname;
-       const char *chroot;
        char *const *argv;
        int port;
        uid_t uid;
@@ -78,6 +82,7 @@ struct nsjconf_t {
        bool clone_newipc;
        bool clone_newuts;
        enum mode_t mode;
+       const char *chroot;
        bool is_root_rw;
        bool is_silent;
        char *iface;
@@ -87,9 +92,7 @@ struct nsjconf_t {
        size_t tmpfs_size;
        bool mount_proc;
         LIST_HEAD(pidslist, pids_t) pids;
-        LIST_HEAD(rwbindmountptslist, constchar_t) rwbindmountpts;
-        LIST_HEAD(robindmountptslist, constchar_t) robindmountpts;
-        LIST_HEAD(tmpfsmountptslist, constchar_t) tmpfsmountpts;
+        LIST_HEAD(mountptslist, mounts_t) mountpts;
 };
 
 #endif                         /* _COMMON_H */
index 37abfa165dc93b3c88379c359b238336df0b32d1..bf1421e2db172d4af2dd6737ba0725431e0d8599 100644 (file)
--- a/contain.c
+++ b/contain.c
@@ -181,125 +181,33 @@ bool containPrepareEnv(struct nsjconf_t * nsjconf)
        return true;
 }
 
-/* findSpecDestination mutates spec (source:dest) to have a null byte instead
- * of ':' in between source and dest, then returns a pointer to the dest
- * string. */
-static char *findSpecDestination(char *spec)
+static bool containMount(struct mounts_t *mpt, const char *dst)
 {
-       char *dest = spec;
-       while (*dest != ':' && *dest != '\0') {
-               dest++;
-       }
-
-       switch (*dest) {
-       case ':':
-               *dest = '\0';
-               return dest + 1;
-       case '\0':
-               return spec;
-       default:
-               // not reached
-               return spec;
-       }
-}
+       LOG_D("Mounting '%s' on '%s' (type:'%s', flags:0x%zx)", mpt->src, dst, mpt->fs_type,
+             mpt->flags);
 
-static bool bindMountRW(struct nsjconf_t *nsjconf, const char *newrootdir, const char *spec)
-{
-       char mount_pt[PATH_MAX];
-       bool success = false;
-       char *source = strdup(spec);
-       if (source == NULL) {
-               PLOG_E("strdup('%s')", spec);
+       if (mkdir(dst, 0711) == -1 && errno != EEXIST) {
+               PLOG_E("mkdir('%s')", dst);
                return false;
        }
-       char *dest = findSpecDestination(source);
-
-       snprintf(mount_pt, sizeof(mount_pt), "%s/%s", newrootdir, dest);
-
-       struct stat st;
-       if (stat(source, &st) == -1) {
-               PLOG_W("stat('%s')", source);
-               goto cleanup;
-       }
-       if (S_ISDIR(st.st_mode)) {
-               // Create mount_pt dir, only if the source bind mount point is also a directory
-               if (mkdir(mount_pt, 0700) == -1 && errno != EEXIST) {
-                       PLOG_E("mkdir('%s') failed. Try creating the '%s/%s' directory manually",
-                              mount_pt, nsjconf->chroot, dest);
-                       goto cleanup;
-               }
-       } else {
-               // For everything else (files, sockets, pipes, devices), create a regular file
-               int fd = open(mount_pt, O_CREAT | O_RDONLY, 0700);
-               if (fd == -1) {
-                       PLOG_E("creat('%s') failed. Try creating the '%s/%s' file manually",
-                              mount_pt, nsjconf->chroot, dest);
-                       goto cleanup;
-               }
-               close(fd);
-       }
-
-       LOG_D("Mounting (bind) '%s' on '%s'", source, mount_pt);
-       if (mount(source, mount_pt, NULL, MS_BIND | MS_REC, NULL) == -1) {
-               PLOG_E("mount('%s', '%s', MS_BIND|MS_REC)", source, mount_pt);
-               goto cleanup;
-       }
-       success = true;
-
- cleanup:
-       free(source);
-       return success;
-}
-
-static bool remountBindMount(const char *spec, unsigned long flags)
-{
-       if (flags == 0ULL) {
-               return true;
-       }
-
-       bool success = false;
-       char *source = strdup(spec);
-       if (source == NULL) {
-               PLOG_E("strdup('%s')", spec);
+       if (mount(mpt->src, dst, mpt->fs_type, mpt->flags, mpt->options) == -1) {
+               PLOG_E("mount('%s', '%s', type='%s')", mpt->src, dst, mpt->fs_type);
                return false;
        }
-       char *dest = findSpecDestination(source);
-
-       LOG_D("Remounting (bind(0x%lx)) '%s' on '%s'", flags, dest, dest);
-       if (mount(dest, dest, NULL, MS_BIND | MS_NOSUID | MS_REMOUNT | MS_PRIVATE | flags, NULL) ==
-           -1) {
-               PLOG_E("mount('%s', '%s', MS_BIND|MS_NOSUID|MS_REMOUNT|MS_PRIVATE|%lu)", dest, dest,
-                      flags);
-               goto cleanup;
-       }
-       success = true;
-
- cleanup:
-       free(source);
-       return success;
+       return true;
 }
 
-static bool containMountProc(struct nsjconf_t *nsjconf, const char *newrootdir)
+static bool containRemountRO(struct mounts_t *mpt)
 {
-       char procrootdir[PATH_MAX];
-       snprintf(procrootdir, sizeof(procrootdir), "%s/proc", newrootdir);
-
-       if (nsjconf->mount_proc == false) {
-               return true;
-       }
-
-       if (nsjconf->mode == MODE_STANDALONE_EXECVE) {
-               if (mount("/proc", procrootdir, NULL, MS_REC | MS_BIND, NULL) == -1) {
-                       PLOG_E("mount('/proc', '%s', MS_REC|MS_BIND)", procrootdir);
+       if (mpt->flags &= MS_RDONLY) {
+               LOG_D("Re-mounting RO '%s'", mpt->dst);
+               if (mount
+                   (mpt->dst, mpt->dst, NULL, MS_BIND | MS_PRIVATE | MS_REMOUNT | MS_RDONLY,
+                    0) == -1) {
+                       PLOG_E("mount('%s', MS_REMOUNT|MS_RDONLY)", mpt->dst);
                        return false;
                }
-               return true;
        }
-       if (mount(NULL, procrootdir, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) == -1) {
-               PLOG_E("mount('%s', 'proc')", procrootdir);
-               return false;
-       }
-
        return true;
 }
 
@@ -329,43 +237,12 @@ bool containMountFS(struct nsjconf_t * nsjconf)
                PLOG_E("mkdir(/tmp/new_root)");
                return false;
        }
-       if (mount(nsjconf->chroot, newrootdir, NULL, MS_BIND | MS_REC, NULL) == -1) {
-               PLOG_E("mount('%s', '%s', MS_BIND | MS_REC)", nsjconf->chroot, newrootdir);
-               return false;
-       }
-       if (containMountProc(nsjconf, newrootdir) == false) {
-               return false;
-       }
 
-       struct constchar_t *p;
-       char tmpfs_size[128];
-       snprintf(tmpfs_size, sizeof(tmpfs_size), "size=%zu", nsjconf->tmpfs_size);
-       LIST_FOREACH(p, &nsjconf->tmpfsmountpts, pointers) {
-               if (strchr(p->value, ':') != NULL) {
-                       PLOG_E("invalid tmpfs mount spec. source:dest format unsupported.");
-                       return false;
-               }
-               char tmpfsdir[PATH_MAX];
-               snprintf(tmpfsdir, sizeof(tmpfsdir), "%s/%s", newrootdir, p->value);
-               if (mkdir(tmpfsdir, 0700) == -1 && errno != EEXIST) {
-                       PLOG_E
-                           ("mkdir('%s') (for tmpfs:'%s'); You probably need to create it inside your "
-                            "--chroot ('%s') directory", tmpfsdir, p->value, nsjconf->chroot);
-                       return false;
-               }
-               LOG_D("Mounting (tmpfs) '%s' at '%s'", p->value, tmpfsdir);
-               if (mount(NULL, tmpfsdir, "tmpfs", 0, tmpfs_size) == -1) {
-                       PLOG_E("mount('%s', 'tmpfs') for '%s'", tmpfsdir, p->value);
-                       return false;
-               }
-       }
-       LIST_FOREACH(p, &nsjconf->robindmountpts, pointers) {
-               if (!bindMountRW(nsjconf, newrootdir, p->value)) {
-                       return false;
-               }
-       }
-       LIST_FOREACH(p, &nsjconf->rwbindmountpts, pointers) {
-               if (!bindMountRW(nsjconf, newrootdir, p->value)) {
+       char dst[PATH_MAX];
+       struct mounts_t *p;
+       LIST_FOREACH(p, &nsjconf->mountpts, pointers) {
+               snprintf(dst, sizeof(dst), "%s/%s", newrootdir, p->dst);
+               if (containMount(p, dst) == false) {
                        return false;
                }
        }
@@ -395,14 +272,8 @@ bool containMountFS(struct nsjconf_t * nsjconf)
                return false;
        }
 
-       if (nsjconf->is_root_rw == false) {
-               if (!remountBindMount("/", MS_RDONLY)) {
-                       return false;
-               }
-       }
-
-       LIST_FOREACH(p, &nsjconf->robindmountpts, pointers) {
-               if (!remountBindMount(p->value, MS_RDONLY)) {
+       LIST_FOREACH(p, &nsjconf->mountpts, pointers) {
+               if (containRemountRO(p) == false) {
                        return false;
                }
        }