caps: simplify capability operations
authorRobert Swiecki <robert@swiecki.net>
Wed, 5 Jul 2017 13:57:07 +0000 (15:57 +0200)
committerRobert Swiecki <robert@swiecki.net>
Wed, 5 Jul 2017 13:57:07 +0000 (15:57 +0200)
caps.c
cmdline.c
common.h
config.c
config.proto
configs/bash-with-fake-geteuid.cfg

diff --git a/caps.c b/caps.c
index f19cdaf6fe470b68df4216ebf5b8c6f658c0ae26..9ea27dbf6272cb4da515faf8d47965c8367a11fe 100644 (file)
--- a/caps.c
+++ b/caps.c
@@ -34,7 +34,7 @@
 /*  *INDENT-OFF* */
 static struct {
        const int val;
-    const char* const name;
+       const char* const name;
 } const capNames[] = {
     VALSTR_STRUCT(CAP_CHOWN),
     VALSTR_STRUCT(CAP_DAC_OVERRIDE),
@@ -104,119 +104,102 @@ static const char *capsValToStr(int val)
        return capsStr;
 }
 
-bool capsInitInternal(struct nsjconf_t * nsjconf, bool is_global)
+static cap_t capsGet(void)
 {
-       cap_t cap_orig = cap_get_pid(getpid());
-       if (cap_orig == NULL) {
-               PLOG_W("capget(PID=%d)", (int)getpid());
-               return false;
+       cap_t cap = cap_get_pid(getpid());
+       if (cap == NULL) {
+               PLOG_F("cap_get_pit(PID=%d)", (int)getpid());
        }
+       return cap;
+}
 
-       cap_t cap_new = cap_dup(cap_orig);
-       if (cap_new == NULL) {
-               PLOG_W("cap_dup()");
-               cap_free(cap_orig);
-               return false;
+static void capsFree(cap_t cap)
+{
+       if (cap_free(cap) == -1) {
+               PLOG_F("cap_free()");
+       }
+}
+
+static void capsClearType(cap_t cap, cap_flag_t type)
+{
+       if (cap_clear_flag(cap, type) == -1) {
+               PLOG_F("cap_clear_flag(flag=%d)", (int)type);
+       }
+}
+
+static cap_flag_value_t capsGetCap(cap_t cap, cap_value_t id, cap_flag_t type)
+{
+       cap_flag_value_t v;
+       if (cap_get_flag(cap, id, type, &v) == -1) {
+               PLOG_F("cap_get_flag(id=%d, type=%d)", (int)id, (int)type);
+       }
+       return v;
+}
+
+static void capsSetCap(cap_t cap, cap_value_t id, cap_value_t type)
+{
+       if (cap_set_flag(cap, type, 1, &id, CAP_SET) == -1) {
+               PLOG_F("cap_set_flag(id=%d, type=%d)", (int)id, (int)type);
        }
+}
+
+static void capsClrFlag(cap_t cap, cap_value_t id, cap_value_t type)
+{
+       if (cap_set_flag(cap, type, 1, &id, CAP_CLEAR) == -1) {
+               PLOG_F("cap_set_flag(id=%d, type=%d)", (int)id, (int)type);
+       }
+}
+
+bool capsInitInternal(struct nsjconf_t *nsjconf, bool is_global)
+{
+       cap_t cap_orig = capsGet();
+       cap_t cap_new = capsGet();
 
        struct capslistt *l = is_global ? &nsjconf->global_caps : &nsjconf->local_caps;
-       if (is_global || nsjconf->keep_caps == false) {
-               if (cap_clear_flag(cap_new, CAP_INHERITABLE) == -1) {
-                       PLOG_W("cap_clear_flag(CAP_INHERITABLE)");
-                       cap_free(cap_orig);
-                       cap_free(cap_new);
-                       return false;
-               }
-               if (is_global == false) {
-                       if (cap_clear_flag(cap_new, CAP_PERMITTED) == -1) {
-                               PLOG_W("cap_clear_flag(CAP_PERMITTED)");
-                               cap_free(cap_orig);
-                               cap_free(cap_new);
-                               return false;
-                       }
-                       if (cap_clear_flag(cap_new, CAP_EFFECTIVE) == -1) {
-                               PLOG_W("cap_clear_flag(CAP_EFFECTIVE)");
-                               cap_free(cap_orig);
-                               cap_free(cap_new);
-                               return false;
+       bool keep_caps = is_global ? nsjconf->keep_global_caps : nsjconf->keep_local_caps;
+
+       if (keep_caps) {
+               for (size_t i = 0; i < ARRAYSIZE(capNames); i++) {
+                       if (capsGetCap(cap_orig, capNames[i].val, CAP_PERMITTED) == CAP_SET) {
+                               capsSetCap(cap_new, capNames[i].val, CAP_INHERITABLE);
+                       } else {
+                               capsClrFlag(cap_new, capNames[i].val, CAP_INHERITABLE);
                        }
                }
-
+       } else {
+               capsClearType(cap_new, CAP_INHERITABLE);
                struct ints_t *p;
                TAILQ_FOREACH(p, l, pointers) {
-                       cap_flag_value_t v;
-                       if (cap_get_flag(cap_orig, p->val, CAP_PERMITTED, &v) == -1) {
-                               PLOG_W("cap_get_flag(cap_orig, CAP_PERMITTED, %s)",
-                                      capsValToStr(p->val));
-                               cap_free(cap_orig);
-                               cap_free(cap_new);
-                               return false;
-                       }
-                       if (v != CAP_SET) {
+                       if (capsGetCap(cap_orig, p->val, CAP_PERMITTED) != CAP_SET) {
                                LOG_W("Capability %s is not permitted in the %s namespace",
                                      capsValToStr(p->val), is_global ? "global" : "local");
-                               cap_free(cap_orig);
-                               cap_free(cap_new);
-                               return false;
-                       }
-                       if (cap_set_flag(cap_new, CAP_INHERITABLE, 1, &p->val, CAP_SET) == -1) {
-                               PLOG_W("cap_set_flag(cap_new, CAP_INHERITABLE, %s)",
-                                      capsValToStr(p->val));
-                               cap_free(cap_orig);
-                               cap_free(cap_new);
-                               return false;
-                       }
-                       if (is_global == false) {
-                               if (cap_set_flag(cap_new, CAP_PERMITTED, 1, &p->val, CAP_SET) == -1) {
-                                       PLOG_W("cap_set_flag(cap_new, CAP_PERMITTED, %s)",
-                                              capsValToStr(p->val));
-                                       cap_free(cap_orig);
-                                       cap_free(cap_new);
-                                       return false;
-                               }
-                               if (cap_set_flag(cap_new, CAP_EFFECTIVE, 1, &p->val, CAP_SET) == -1) {
-                                       PLOG_W("cap_set_flag(cap_new, CAP_EFFECTIVE, %s)",
-                                              capsValToStr(p->val));
-                                       cap_free(cap_orig);
-                                       cap_free(cap_new);
-                                       return false;
-                               }
-                       }
-               }
-       }
-
-       if (is_global == false || nsjconf->keep_caps == true) {
-               for (size_t i = 0; i < ARRAYSIZE(capNames); i++) {
-                       cap_flag_value_t v;
-                       if (cap_get_flag(cap_orig, capNames[i].val, CAP_PERMITTED, &v) == -1) {
-                               PLOG_W("cap_get_flag(cap_orig, CAP_PERMITTED, %s)",
-                                      capNames[i].name);
-                               cap_free(cap_orig);
-                               cap_free(cap_new);
-                               return false;
-                       }
-                       if (cap_set_flag(cap_new, CAP_INHERITABLE, 1, &capNames[i].val, v) == -1) {
-                               PLOG_W("cap_set_flag(cap_new, CAP_INHERITABLE, %s)",
-                                      capNames[i].name);
-                               cap_free(cap_orig);
-                               cap_free(cap_new);
+                               capsFree(cap_orig);
+                               capsFree(cap_new);
                                return false;
                        }
+                       capsSetCap(cap_new, p->val, CAP_INHERITABLE);
                }
        }
 
        if (cap_set_proc(cap_new) == -1) {
-               PLOG_W("cap_set_proc()");
-               cap_free(cap_orig);
-               cap_free(cap_new);
+               capsFree(cap_orig);
+               capsFree(cap_new);
                return false;
        }
-#if defined(PR_CAP_AMBIENT)
-       if (is_global == false && nsjconf->keep_caps == true) {
-               for (unsigned long i = 0; i < CAP_LAST_CAP; i++) {
-                       if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0UL, 0UL) == -1) {
+#if !defined(PR_CAP_AMBIENT)
+#define PR_CAP_AMBIENT 47
+#define PR_CAP_AMBIENT_RAISE 2
+#endif                         /* !defined(PR_CAP_AMBIENT) */
+       if (keep_caps) {
+               for (size_t i = 0; i < ARRAYSIZE(capNames); i++) {
+                       if (capsGetCap(cap_orig, capNames[i].val, CAP_PERMITTED) != CAP_SET) {
+                               continue;
+                       }
+                       if (prctl
+                           (PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, (unsigned long)capNames[i].val,
+                            0UL, 0UL) == -1) {
                                PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %s)",
-                                      capsValToStr(i));
+                                      capNames[i].name);
                        }
                }
        } else {
@@ -230,10 +213,9 @@ bool capsInitInternal(struct nsjconf_t * nsjconf, bool is_global)
                        }
                }
        }
-#endif                         /* defined(PR_CAP_AMBIENT) */
 
-       cap_free(cap_orig);
-       cap_free(cap_new);
+       capsFree(cap_orig);
+       capsFree(cap_new);
        return true;
 }
 
index 26e5a3600bd7746fa626c52f28439a9b310d1b67..724014f50c7245526782e520dffa28b30b606c59 100644 (file)
--- a/cmdline.c
+++ b/cmdline.c
@@ -86,7 +86,8 @@ struct custom_option custom_opts[] = {
     {{"quiet", no_argument, NULL, 'q'}, "Only output warning and more important messages"},
     {{"keep_env", no_argument, NULL, 'e'}, "Should all environment variables be passed to the child?"},
     {{"env", required_argument, NULL, 'E'}, "Environment variable (can be used multiple times)"},
-    {{"keep_caps", no_argument, NULL, 0x0501}, "Don't drop capabilities (DANGEROUS)"},
+    {{"keep_global_caps", no_argument, NULL, 0x0500}, "Don't drop capabilities in the global namespace"},
+    {{"keep_local_caps", no_argument, NULL, 0x0501}, "Don't drop capabilities in the local namespace"},
     {{"silent", no_argument, NULL, 0x0502}, "Redirect child's fd:0/1/2 to /dev/null"},
     {{"skip_setsid", no_argument, NULL, 0x0504}, "Don't call setsid(), allows for terminal signal handling in the sandboxed process"},
     {{"pass_fd", required_argument, NULL, 0x0505}, "Don't close this FD before executing child (can be specified multiple times), by default: 0/1/2 are kept open"},
@@ -141,6 +142,7 @@ struct custom_option deprecated_opts[] = {
     {{"iface_vs_ip", required_argument, NULL, 0x701}, "IP of the 'vs' interface (e.g. \"192.168.0.1\")"},
     {{"iface_vs_nm", required_argument, NULL, 0x702}, "Netmask of the 'vs' interface (e.g. \"255.255.255.0\")"},
     {{"iface_vs_gw", required_argument, NULL, 0x703}, "Default GW for the 'vs' interface (e.g. \"192.168.0.1\")"},
+    {{"keep_caps", no_argument, NULL, 0x0501}, "Don't drop capabilities in the local namespace"},
 };
 /*  *INDENT-ON* */
 
@@ -215,16 +217,16 @@ void cmdlineLogParams(struct nsjconf_t *nsjconf)
              "bind:[%s]:%d, "
              "max_conns_per_ip:%u, time_limit:%ld, personality:%#lx, daemonize:%s, "
              "clone_newnet:%s, clone_newuser:%s, clone_newns:%s, clone_newpid:%s, "
-             "clone_newipc:%s, clonew_newuts:%s, clone_newcgroup:%s, keep_caps:%s, "
-             "tmpfs_size:%zu, disable_no_new_privs:%s, max_cpus:%zu",
+             "clone_newipc:%s, clonew_newuts:%s, clone_newcgroup:%s, keep_global_caps:%s, "
+             "keep_local_caps:%s, tmpfs_size:%zu, disable_no_new_privs:%s, max_cpus:%zu",
              nsjconf->hostname, nsjconf->chroot ? nsjconf->chroot : "[NULL]", nsjconf->argv[0],
              nsjconf->bindhost, nsjconf->port, nsjconf->max_conns_per_ip, nsjconf->tlimit,
              nsjconf->personality, logYesNo(nsjconf->daemonize), logYesNo(nsjconf->clone_newnet),
              logYesNo(nsjconf->clone_newuser), logYesNo(nsjconf->clone_newns),
              logYesNo(nsjconf->clone_newpid), logYesNo(nsjconf->clone_newipc),
              logYesNo(nsjconf->clone_newuts), logYesNo(nsjconf->clone_newcgroup),
-             logYesNo(nsjconf->keep_caps), nsjconf->tmpfs_size,
-             logYesNo(nsjconf->disable_no_new_privs), nsjconf->max_cpus);
+             logYesNo(nsjconf->keep_global_caps), logYesNo(nsjconf->keep_local_caps),
+             nsjconf->tmpfs_size, logYesNo(nsjconf->disable_no_new_privs), nsjconf->max_cpus);
 
        {
                struct mounts_t *p;
@@ -320,7 +322,8 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
       .daemonize = false,
       .tlimit = 0,
       .max_cpus = 0,
-      .keep_caps = false,
+      .keep_global_caps = false,
+      .keep_local_caps = false,
       .disable_no_new_privs = false,
       .rl_as = 512 * (1024 * 1024),
       .rl_core = 0,
@@ -526,8 +529,11 @@ bool cmdlineParse(int argc, char *argv[], struct nsjconf_t * nsjconf)
                case 0x0407:
                        nsjconf->clone_newcgroup = true;
                        break;
+               case 0x0500:
+                       nsjconf->keep_global_caps = true;
+                       break;
                case 0x0501:
-                       nsjconf->keep_caps = true;
+                       nsjconf->keep_local_caps = true;
                        break;
                case 0x0502:
                        nsjconf->is_silent = true;
index 61d4a1dd082020d614d62084990e3c7968382545..18b58e2183fa490f3b16104c620f7c911d15601b 100644 (file)
--- a/common.h
+++ b/common.h
@@ -127,7 +127,8 @@ struct nsjconf_t {
        time_t tlimit;
        size_t max_cpus;
        bool keep_env;
-       bool keep_caps;
+       bool keep_global_caps;
+       bool keep_local_caps;
        bool disable_no_new_privs;
        __rlim64_t rl_as;
        __rlim64_t rl_core;
index 2696e5dfb90b4fd9b5243e1d7061c05ac7a4ba1b..af9bdc0ea218120f175db857b18e0d791e943bd2 100644 (file)
--- a/config.c
+++ b/config.c
@@ -112,7 +112,8 @@ static bool configParseInternal(struct nsjconf_t *nsjconf, Nsjail__NsJailConfig
                TAILQ_INSERT_TAIL(&nsjconf->envs, p, pointers);
        }
 
-       nsjconf->keep_caps = njc->keep_caps;
+       nsjconf->keep_local_caps = njc->keep_local_caps;
+       nsjconf->keep_global_caps = njc->keep_global_caps;
        nsjconf->is_silent = njc->silent;
        nsjconf->skip_setsid = njc->skip_setsid;
 
index 2d053fc30924b9e24969fecb67d268f992f6eed1..5bce133243a1678ef101a5ba75ed84ad770e2328 100644 (file)
@@ -108,8 +108,10 @@ message NsJailConfig
     /* EnvVars to be set before executing binaries */
     repeated string envar = 20;
 
-    /* Should capabilities be preserved or dropped */
-    required bool keep_caps = 21 [ default = false ];
+    /* Should local (inside ns) capabilities be preserved or dropped */
+    required bool keep_local_caps = 21 [ default = false ];
+    /* Should global (outside ns) capabilities be preserved or dropped */
+    required bool keep_global_caps = 63 [ default = false ];
     /* Should nsjail close FD=0,1,2 before executing the process */
     required bool silent = 22 [ default = false ];
     /* Should the child process have control over terminal?
index 17a2fd0d81e164ccf03291e97c097e599d2aeef1..7437a446250e08a22006002b46203b851505fffb 100644 (file)
@@ -28,7 +28,7 @@ envar: "TERM=linux"
 envar: "HOME=/"
 envar: "PS1=[\\H:\\t:\\s-\\V:\\w]\\$ "
 
-keep_caps: true
+keep_local_caps: true
 silent: false
 skip_setsid: true
 pass_fd: 100