From 701825970ae363705714ad976245ae8fd0f0883c Mon Sep 17 00:00:00 2001 From: Jagger Date: Sat, 15 Aug 2015 16:02:38 +0200 Subject: [PATCH] Implementation of MODE_STANDALONE_EXECVE --- cmdline.c | 40 ++++++++++++++++++++++++++-------------- common.h | 2 ++ contain.c | 34 ++++++++++++++++++++++++++++------ log.c | 2 +- nsjail.c | 8 ++++++-- subproc.c | 17 ++++++++++++++++- 6 files changed, 79 insertions(+), 24 deletions(-) diff --git a/cmdline.c b/cmdline.c index 1f207ff..a2987c3 100644 --- a/cmdline.c +++ b/cmdline.c @@ -74,6 +74,9 @@ void cmdlineLogParams(struct nsjconf_t *nsjconf) case MODE_STANDALONE_ONCE: LOG_I("Mode: STANDALONE_ONCE"); break; + case MODE_STANDALONE_EXECVE: + LOG_I("Mode: STANDALONE_EXECVE"); + break; case MODE_STANDALONE_RERUN: LOG_I("Mode: STANDALONE_RERUN"); break; @@ -183,6 +186,7 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf) .initial_gid = getgid(), .max_conns_per_ip = 0, .tmpfs_size = 4 * (1024 * 1024), + .mount_proc = true, }; /* *INDENT-OFF* */ @@ -200,7 +204,8 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf) {{"help", no_argument, NULL, 'h'}, "Help plz.."}, {{"mode", required_argument, NULL, 'M'}, "Execution mode (default: l [MODE_LISTEN_TCP]):\n" "\tl: Listen to connections on a TCP port (specified with --port) [MODE_LISTEN_TCP]\n" - "\to: Immediately launch a single process on a console [MODE_STANDALONE_ONCE]\n" + "\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')"}, {{"user", required_argument, NULL, 'u'}, "Username/uid of processess inside the jail (default: 'nobody')"}, @@ -213,7 +218,9 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf) {{"daemon", no_argument, NULL, 'd'}, "Daemonize after start? (default: false)"}, {{"verbose", no_argument, NULL, 'v'}, "Verbose output (default: false)"}, {{"keep_env", no_argument, NULL, 'e'}, "Should all environment variables be passed to the child? (default: false)"}, - {{"keep_caps", no_argument, NULL, 0x0502}, "Don't drop capabilities (DANGEROUS) (default: false)"}, + {{"keep_caps", no_argument, NULL, 0x0501}, "Don't drop capabilities (DANGEROUS) (default: false)"}, + {{"silent", no_argument, NULL, 0x0502}, "Redirect child's fd:0/1/2 to /dev/null (default: false)"}, + {{"disable_sandbox", no_argument, NULL, 0x0503}, "Don't enable the seccomp-bpf sandboxing (default: false)"}, {{"rlimit_as", required_argument, NULL, 0x0201}, "RLIMIT_AS in MB, 'max' for RLIM_INFINITY, 'def' for the current value (default: 512)"}, {{"rlimit_core", required_argument, NULL, 0x0202}, "RLIMIT_CORE in MB, 'max' for RLIM_INFINITY, 'def' for the current value (default: 0)"}, {{"rlimit_cpu", required_argument, NULL, 0x0203}, "RLIMIT_CPU, 'max' for RLIM_INFINITY, 'def' for the current value (default: 600)"}, @@ -232,14 +239,13 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf) {{"disable_clone_newpid", no_argument, NULL, 0x0404}, "Don't use CLONE_NEWPID (default: false)"}, {{"disable_clone_newipc", no_argument, NULL, 0x0405}, "Don't use CLONE_NEWIPC (default: false)"}, {{"disable_clone_newuts", no_argument, NULL, 0x0406}, "Don't use CLONE_NEWUTS (default: false)"}, - {{"disable_sandbox", no_argument, NULL, 0x0501}, "Don't enable the seccomp-bpf sandboxing (default: false)"}, - {{"rw", no_argument, NULL, 0x0503}, "Mount / as RW (default: RO)"}, - {{"silent", no_argument, NULL, 0x0504}, "Redirect child's fd:0/1/2 to /dev/null (default: false)"}, + {{"rw", no_argument, NULL, 0x0601}, "Mount / as RW (default: RO)"}, {{"bindmount_ro", required_argument, NULL, 'R'}, "List of mountpoints to be mounted --bind (ro) inside the container. Can be specified multiple times. Supports 'source' syntax, or 'source:dest'. (default: none)"}, {{"bindmount", required_argument, NULL, 'B'}, "List of mountpoints to be mounted --bind (rw) inside the container. Can be specified multiple times. Supports 'source' syntax, or 'source:dest'. (default: none)"}, {{"tmpfsmount", required_argument, NULL, 'T'}, "List of mountpoints to be mounted as RW/tmpfs inside the container. Can be specified multiple times. Supports 'dest' syntax. (default: none)"}, {{"iface", required_argument, NULL, 'I'}, "Interface which will be cloned (MACVTAP) and put inside the subprocess' namespace"}, - {{"tmpfs_size", required_argument, NULL, 0x0506}, "Number of bytes to allocate for tmpfsmounts in bytes (default: 4194304)"}, + {{"tmpfs_size", required_argument, NULL, 0x0602}, "Number of bytes to allocate for tmpfsmounts (default: 4194304)"}, + {{"disable_proc", no_argument, NULL, 0x0603}, "Disable mounting /proc (default: false)"}, {{0, 0, 0, 0}, NULL}, }; /* *INDENT-ON* */ @@ -268,9 +274,6 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf) case 'i': nsjconf->max_conns_per_ip = strtoul(optarg, NULL, 0); break; - case 0x0506: - nsjconf->tmpfs_size = strtoull(optarg, NULL, 0); - break; case 'u': user = optarg; break; @@ -351,16 +354,22 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf) nsjconf->clone_newuts = false; break; case 0x0501: - nsjconf->apply_sandbox = false; - break; - case 0x0502: nsjconf->keep_caps = true; break; + case 0x0502: + nsjconf->is_silent = true; + break; case 0x0503: + nsjconf->apply_sandbox = false; + break; + case 0x0601: nsjconf->is_root_rw = true; break; - case 0x0504: - nsjconf->is_silent = true; + case 0x0602: + nsjconf->tmpfs_size = strtoull(optarg, NULL, 0); + break; + case 0x0603: + nsjconf->mount_proc = false; break; case 'R': { @@ -400,6 +409,9 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf) case 'o': nsjconf->mode = MODE_STANDALONE_ONCE; break; + case 'e': + nsjconf->mode = MODE_STANDALONE_EXECVE; + break; case 'r': nsjconf->mode = MODE_STANDALONE_RERUN; break; diff --git a/common.h b/common.h index 95e9349..ae30388 100644 --- a/common.h +++ b/common.h @@ -46,6 +46,7 @@ struct constchar_t { enum mode_t { MODE_LISTEN_TCP = 0, MODE_STANDALONE_ONCE, + MODE_STANDALONE_EXECVE, MODE_STANDALONE_RERUN }; @@ -84,6 +85,7 @@ struct nsjconf_t { gid_t initial_gid; unsigned int max_conns_per_ip; 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; diff --git a/contain.c b/contain.c index e9d5a72..d4430a9 100644 --- a/contain.c +++ b/contain.c @@ -270,6 +270,30 @@ static bool remountBindMount(const char *spec, unsigned long flags) return success; } +static bool containMountProc(struct nsjconf_t *nsjconf, const char *newrootdir) +{ + 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); + 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; +} + bool containMountFS(struct nsjconf_t * nsjconf) { const char *destdir = "/tmp"; @@ -287,11 +311,7 @@ bool containMountFS(struct nsjconf_t * nsjconf) PLOG_E("mount('%s', '%s', MS_BIND | MS_REC)", nsjconf->chroot, newrootdir); return false; } - - char procrootdir[PATH_MAX]; - snprintf(procrootdir, sizeof(procrootdir), "%s/proc", newrootdir); - if (mount(NULL, procrootdir, "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV, NULL) == -1) { - PLOG_E("mount('%s', 'proc')", procrootdir); + if (containMountProc(nsjconf, newrootdir) == false) { return false; } @@ -455,7 +475,9 @@ bool containMakeFdsCOE(void) bool containSetupFD(struct nsjconf_t * nsjconf, int fd_in, int fd_out, int fd_err, int fd_log) { /* Make sure all logs go to the parent process from now on */ - logRedirectLogFD(fd_log); + if (fd_log != -1) { + logRedirectLogFD(fd_log); + } if (nsjconf->mode != MODE_LISTEN_TCP) { if (nsjconf->is_silent == false) { diff --git a/log.c b/log.c index 19f6d5e..ca4559a 100644 --- a/log.c +++ b/log.c @@ -52,7 +52,7 @@ bool logInitLogFile(struct nsjconf_t *nsjconf, const char *logfile, bool is_verb logfile = _LOG_DEFAULT_FILE; } if (logfile == NULL) { - logfile = "/dev/tty"; + logfile = "/proc/self/fd/2"; } log_fd = open(logfile, O_CREAT | O_RDWR | O_APPEND, 0640); if (log_fd == -1) { diff --git a/nsjail.c b/nsjail.c index b1acb17..1bf0371 100644 --- a/nsjail.c +++ b/nsjail.c @@ -92,8 +92,12 @@ static bool nsjailSetSigHandlers(void) return true; } -static bool nsjailSetTimer(void) +static bool nsjailSetTimer(struct nsjconf_t *nsjconf) { + if (nsjconf->mode == MODE_STANDALONE_EXECVE) { + return true; + } + struct itimerval it = { .it_value = {.tv_sec = 1,.tv_usec = 0}, .it_interval = {.tv_sec = 1,.tv_usec = 0}, @@ -173,7 +177,7 @@ int main(int argc, char *argv[]) if (nsjailSetSigHandlers() == false) { exit(1); } - if (nsjailSetTimer() == false) { + if (nsjailSetTimer(&nsjconf) == false) { exit(1); } diff --git a/subproc.c b/subproc.c index ff16f96..3e75bdf 100644 --- a/subproc.c +++ b/subproc.c @@ -204,7 +204,7 @@ void subprocRunChild(struct nsjconf_t *nsjconf, int fd_in, int fd_out, int fd_er return; } - unsigned int flags = SIGCHLD; + unsigned int flags = 0UL; flags |= (nsjconf->clone_newnet ? CLONE_NEWNET : 0); flags |= (nsjconf->clone_newuser ? CLONE_NEWUSER : 0); flags |= (nsjconf->clone_newns ? CLONE_NEWNS : 0); @@ -212,6 +212,21 @@ void subprocRunChild(struct nsjconf_t *nsjconf, int fd_in, int fd_out, int fd_er flags |= (nsjconf->clone_newipc ? CLONE_NEWIPC : 0); flags |= (nsjconf->clone_newuts ? CLONE_NEWUTS : 0); + if (nsjconf->mode == MODE_STANDALONE_EXECVE) { + if (nsjconf->clone_newpid) { + LOG_W("CLONE_NEWPID requested. It causes troubles with unshare() " + "[ENOMEM with clone/fork/vfork]. Disabling it"); + flags &= ~(CLONE_NEWPID); + } + LOG_D("Entering namespace with flags: %#x", flags); + if (unshare(flags) == -1) { + PLOG_E("unshare(%u)", flags); + _exit(EXIT_FAILURE); + } + subprocNewProc(nsjconf, fd_in, fd_out, fd_err, -1); + } + + flags |= SIGCHLD; LOG_D("Creating new process with clone flags: %#x", flags); int pipefd[2];