#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>
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);
}
}
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,
/* *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[] = {
"\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')"},
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':
}
}
+ 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;
}
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;
}
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;
}
}
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;
}
}