From 59cedfe10f206fc27fc02efc3af12d405b7ce94e Mon Sep 17 00:00:00 2001 From: Jagger Date: Sat, 17 Oct 2015 16:48:30 +0200 Subject: [PATCH] Use just a single list for mount-points (RO, RW, chroot) --- cmdline.c | 112 +++++++++++++++++++++++++++-------- common.h | 17 +++--- contain.c | 173 +++++++----------------------------------------------- 3 files changed, 118 insertions(+), 184 deletions(-) diff --git a/cmdline.c b/cmdline.c index 3969400..6f1eac2 100644 --- a/cmdline.c +++ b/cmdline.c @@ -31,8 +31,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -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; } diff --git a/common.h b/common.h index 5710206..0b34db0 100644 --- a/common.h +++ b/common.h @@ -22,6 +22,7 @@ #ifndef _COMMON_H #define _COMMON_H +#include #include #include #include @@ -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 */ diff --git a/contain.c b/contain.c index 37abfa1..bf1421e 100644 --- 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; } } -- 2.34.1