jail: capabilities: apply in two phases
authorDaniel Golle <daniel@makrotopia.org>
Mon, 19 Oct 2020 18:30:13 +0000 (19:30 +0100)
committerDaniel Golle <daniel@makrotopia.org>
Wed, 21 Oct 2020 12:22:35 +0000 (13:22 +0100)
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
jail/capabilities.c
jail/capabilities.h
jail/jail.c

index 2eb154e77d0a9ca56808d8178572b0f52f81afb7..b3627461b642bd27175f7ffd19c0dba7aae02603 100644 (file)
@@ -117,7 +117,7 @@ int parseOCIcapabilities(struct jail_capset *capset, struct blob_attr *msg)
 }
 
 
-int applyOCIcapabilities(struct jail_capset ocicapset)
+int applyOCIcapabilities(struct jail_capset ocicapset, uint64_t retain)
 {
        struct __user_cap_header_struct uh = {};
        struct __user_cap_data_struct ud;
@@ -137,7 +137,7 @@ int applyOCIcapabilities(struct jail_capset ocicapset)
 
                                continue;
                        }
-                       if ( (ocicapset.bounding & (1LLU << cap)) == 0) {
+                       if ( ((ocicapset.bounding | retain) & (1LLU << cap)) == 0) {
                                DEBUG("dropping capability %s (%d) from bounding set\n", capabilities_names[cap], cap);
                                if (prctl(PR_CAPBSET_DROP, cap, 0, 0, 0)) {
                                        ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %m\n", cap);
@@ -161,10 +161,10 @@ int applyOCIcapabilities(struct jail_capset ocicapset)
        DEBUG("old capabilities: Pe=%08x Pp=%08x Pi=%08x\n", ud.effective, ud.permitted, ud.inheritable);
 
        if (ocicapset.effective != JAIL_CAP_ALL)
-               ud.effective = ocicapset.effective;
+               ud.effective = ocicapset.effective | retain;
 
        if (ocicapset.permitted != JAIL_CAP_ALL)
-               ud.permitted = ocicapset.permitted;
+               ud.permitted = ocicapset.permitted | retain;
 
        if (ocicapset.inheritable != JAIL_CAP_ALL)
                ud.inheritable = ocicapset.inheritable;
@@ -208,8 +208,6 @@ int parseOCIcapabilities_from_file(struct jail_capset *capset, const char *file)
        struct blob_buf b = { 0 };
        int ret;
 
-       DEBUG("dropping capabilities\n");
-
        blob_buf_init(&b, 0);
        ret = !blobmsg_add_json_from_file(&b, file);
        if (ret) {
index 7185fd482a8d29b5fc02e6fff4e712a349254dfb..d8c6b8d60b56e5285b54df2c45a2de9f9e87d1d9 100644 (file)
@@ -27,7 +27,7 @@ struct jail_capset {
 
 int parseOCIcapabilities(struct jail_capset *capset, struct blob_attr *msg);
 int parseOCIcapabilities_from_file(struct jail_capset *capset, const char *file);
-int applyOCIcapabilities(struct jail_capset capset);
+int applyOCIcapabilities(struct jail_capset capset, uint64_t retain);
 
 /* capget/capset syscall wrappers are provided by libc */
 extern int capget(cap_user_header_t header, cap_user_data_t data);
index ede09442487f25df33345b91de5a9357c941cde6..08e95e9903fce2190a03a99ed5198ecf187908d5 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/filter.h>
 #include <linux/limits.h>
 #include <linux/nsfs.h>
+#include <linux/securebits.h>
 #include <signal.h>
 #include <inttypes.h>
 
@@ -1172,15 +1173,20 @@ static void post_jail_fs(void)
 
 static void post_start_hook(void)
 {
-       if (applyOCIcapabilities(opts.capset))
+       int pw_uid, pw_gid, gr_gid;
+
+       if (prctl(PR_SET_SECUREBITS, SECBIT_NO_SETUID_FIXUP)) {
+               ERROR("prctl(PR_SET_SECUREBITS) failed: %m\n");
                exit(EXIT_FAILURE);
+       }
 
-       if (!(opts.namespace & CLONE_NEWUSER) && (opts.setns.user == -1)) {
-               int pw_uid, pw_gid, gr_gid;
-               get_jail_user(&pw_uid, &pw_gid, &gr_gid);
+       /* drop capabilities, retain those still needed to further setup jail */
+       if (applyOCIcapabilities(opts.capset, (1LLU << CAP_SETGID) | (1LLU << CAP_SETUID) | (1LLU << CAP_SETPCAP)))
+               exit(EXIT_FAILURE);
 
-               set_jail_user(opts.pw_uid?:pw_uid, opts.pw_gid?:pw_gid, opts.gr_gid?:gr_gid);
-       }
+       /* use either cmdline-supplied user/group or uid/gid from OCI spec */
+       get_jail_user(&pw_uid, &pw_gid, &gr_gid);
+       set_jail_user(opts.pw_uid?:pw_uid, opts.pw_gid?:pw_gid, opts.gr_gid?:gr_gid);
 
        if (opts.additional_gids &&
            (setgroups(opts.num_additional_gids, opts.additional_gids) < 0)) {
@@ -1191,8 +1197,17 @@ static void post_start_hook(void)
        if (opts.set_umask)
                umask(opts.umask);
 
+       if (prctl(PR_SET_SECUREBITS, 0)) {
+               ERROR("prctl(PR_SET_SECUREBITS) failed: %m\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /* drop remaining capabilities to end up with specified sets */
+       if (applyOCIcapabilities(opts.capset, 0))
+               exit(EXIT_FAILURE);
+
        if (opts.no_new_privs && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
-                ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %m\n");
+               ERROR("prctl(PR_SET_NO_NEW_PRIVS) failed: %m\n");
                exit(EXIT_FAILURE);
        }