cmdline: add option --execute_fd and support for it, in order to use execveat()
authorRobert Swiecki <robert@swiecki.net>
Wed, 18 Oct 2017 15:57:52 +0000 (17:57 +0200)
committerRobert Swiecki <robert@swiecki.net>
Wed, 18 Oct 2017 15:57:52 +0000 (17:57 +0200)
cmdline.c
config.cc
config.proto
configs/busybox-with-execveat.cfg [new file with mode: 0644]
nsjail.h
subproc.c

index aedbdab..aed4d1f 100644 (file)
--- a/cmdline.c
+++ b/cmdline.c
@@ -36,6 +36,8 @@
 #include <strings.h>
 #include <sys/mount.h>
 #include <sys/personality.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
 #include <unistd.h>
 
@@ -63,6 +65,7 @@ struct custom_option custom_opts[] = {
         "\tr: Launch a single process on the console with clone/execve, keep doing it forever [MODE_STANDALONE_RERUN]" },
     { { "config", required_argument, NULL, 'C' }, "Configuration file in the config.proto ProtoBuf format (see configs/ directory for examples)" },
     { { "exec_file", required_argument, NULL, 'x' }, "File to exec (default: argv[0])" },
+    { { "execute_fd", no_argument, NULL, 0x0607 }, "Use execveat() to execute a file-descriptor instead of executing the binary path. In such case argv[0]/exec_file denotes a file path before mount namespacing" },
     { { "chroot", required_argument, NULL, 'c' }, "Directory containing / of the jail (default: none)" },
     { { "rw", no_argument, NULL, 0x601 }, "Mount chroot dir (/) R/W (default: R/O)" },
     { { "user", required_argument, NULL, 'u' }, "Username/uid of processess inside the jail (default: your current uid). You can also use inside_ns_uid:outside_ns_uid:count convention here. Can be specified multiple times" },
@@ -312,10 +315,12 @@ bool cmdlineParse(int argc, char* argv[], struct nsjconf_t* nsjconf)
 {
        (*nsjconf) = (const struct nsjconf_t){
                .exec_file = NULL,
+               .use_execveat = false,
+               .exec_fd = -1,
+               .argv = NULL,
                .hostname = "NSJAIL",
                .cwd = "/",
                .chroot = NULL,
-               .argv = NULL,
                .port = 0,
                .bindhost = "::",
                .log_fd = STDERR_FILENO,
@@ -556,7 +561,7 @@ bool cmdlineParse(int argc, char* argv[], struct nsjconf_t* nsjconf)
                case 0x0508:
                        nsjconf->max_cpus = strtoul(optarg, NULL, 0);
                        break;
-               case 0x509: {
+               case 0x0509: {
                        struct ints_t* f = utilMalloc(sizeof(struct ints_t));
                        f->val = capsNameToVal(optarg);
                        if (f->val == -1) {
@@ -581,6 +586,9 @@ bool cmdlineParse(int argc, char* argv[], struct nsjconf_t* nsjconf)
                case 0x0606:
                        nsjconf->is_proc_rw = true;
                        break;
+               case 0x0607:
+                       nsjconf->use_execveat = true;
+                       break;
                case 'E': {
                        struct charptr_t* p = utilMalloc(sizeof(struct charptr_t));
                        p->val = optarg;
@@ -799,5 +807,18 @@ bool cmdlineParse(int argc, char* argv[], struct nsjconf_t* nsjconf)
                nsjconf->exec_file = nsjconf->argv[0];
        }
 
+       if (nsjconf->use_execveat) {
+#if !defined(__NR_execveat)
+               LOG_E("Your nsjail is compiled without support for the execveat() syscall, yet you "
+                     "specified --execute_fd flag");
+               return false;
+#endif /* !defined(__NR_execveat) */
+               if ((nsjconf->exec_fd = open(nsjconf->exec_file, O_RDONLY | O_PATH | O_CLOEXEC))
+                   == -1) {
+                       PLOG_W("Couldn't open '%s' file", nsjconf->exec_file);
+                       return false;
+               }
+       }
+
        return true;
 }
index 95f4270..91fa959 100644 (file)
--- a/config.cc
+++ b/config.cc
@@ -296,6 +296,7 @@ static bool configParseInternal(struct nsjconf_t* nsjconf, const nsjail::NsJailC
                argv.push_back(nullptr);
                nsjconf->exec_file = DUP_IF_SET(njc.exec_bin(), path);
                nsjconf->argv = argv.data();
+               nsjconf->use_execveat = njc.exec_bin().exec_fd();
        }
 
        return true;
index 9bd04bc..3c727f3 100644 (file)
@@ -68,6 +68,8 @@ message Exe
     repeated string arg = 2;
     /* Override argv[0] */
     optional string arg0 = 3;
+    /* Should execveat() be used to execute a file-descriptor instead? */
+    optional bool exec_fd = 4 [ default = false ];
 }
 message NsJailConfig
 {
diff --git a/configs/busybox-with-execveat.cfg b/configs/busybox-with-execveat.cfg
new file mode 100644 (file)
index 0000000..af2402f
--- /dev/null
@@ -0,0 +1,48 @@
+name: "busybox-with-execveat"
+description: "An example/demo policy which allows to execute /bin/busybox in an empty (only /proc) "
+description: "mount namespace which doesn't even include busybox itself."
+
+mode: ONCE
+hostname: "BUSYBOX"
+cwd: "/"
+
+time_limit: 100
+
+keep_env: false
+envar: "TERM=linux"
+envar: "PS1=$ "
+
+skip_setsid: true
+
+clone_newcgroup: true
+
+uidmap {
+       inside_id: "999999"
+       outside_id: ""
+       count: 1
+}
+
+gidmap {
+       inside_id: "999999"
+       outside_id: ""
+       count: 1
+}
+
+mount_proc: false
+
+mount {
+       dst: "/proc"
+       fstype: "proc"
+       rw: false
+}
+
+seccomp_string: "POLICY example {                              "
+seccomp_string:        "       ERRNO(0) { ptrace }             "
+seccomp_string:        "}                                                              "
+seccomp_string:        "USE example DEFAULT ALLOW"
+
+exec_bin {
+       path: "/bin/busybox"
+       arg: "sh"
+       exec_fd: true
+}
index d5f894f..b094acf 100644 (file)
--- a/nsjail.h
+++ b/nsjail.h
@@ -105,9 +105,12 @@ enum llevel_t {
 
 struct nsjconf_t {
        const char* exec_file;
+       bool use_execveat;
+       int exec_fd;
+       const char** argv;
        const char* hostname;
        const char* cwd;
-       const char** argv;
+       const char* chroot;
        int port;
        const char* bindhost;
        int log_fd;
@@ -135,7 +138,6 @@ struct nsjconf_t {
        bool clone_newuts;
        bool clone_newcgroup;
        enum ns_mode_t mode;
-       const char* chroot;
        bool is_root_rw;
        bool is_silent;
        bool skip_setsid;
index 61c2726..5d2cd48 100644 (file)
--- a/subproc.c
+++ b/subproc.c
@@ -36,6 +36,7 @@
 #include <string.h>
 #include <sys/queue.h>
 #include <sys/socket.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <time.h>
@@ -170,7 +171,17 @@ static int subprocNewProc(struct nsjconf_t* nsjconf, int fd_in, int fd_out, int
        if (sandboxApply(nsjconf) == false) {
                exit(0xff);
        }
-       execv(nsjconf->exec_file, (char* const*)&nsjconf->argv[0]);
+
+       if (nsjconf->use_execveat) {
+#if defined(__NR_execveat)
+               syscall(__NR_execveat, (uintptr_t)nsjconf->exec_fd, "",
+                   (char* const*)&nsjconf->argv[0], environ, (uintptr_t)AT_EMPTY_PATH);
+#else /* defined(__NR_execveat) */
+               LOG_F("Your system doesn't support execveat() syscall");
+#endif /* defined(__NR_execveat) */
+       } else {
+               execv(nsjconf->exec_file, (char* const*)&nsjconf->argv[0]);
+       }
 
        PLOG_E("execve('%s') failed", nsjconf->exec_file);