capabilities: make sure that CAP_SETPCAP is cleared
When we didn't require CAP_SETPCAP, make sure we drop it when we're
finished manipulating the bounding set.
Additionally, fixes the capability bit tests for caps larger than
32-bits. The compiler didn't know to warn about the potentially out-of-range
<<-operator usage.
BUG=chromium-os:38643
TEST=link build, security_Minijail0 passes, verified CAP_SETPCAP is missing:
`minijail0 -c 0 /bin/cat /proc/self/status | grep CapEff` is all zeros
`minijail0 -c 1 /bin/cat /proc/self/status | grep CapEff` is 1
Change-Id: I7c0722c3bc775164486ff9628fc0c2005ae9275d
Signed-off-by: Kees Cook <keescook@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/42670
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
diff --git a/libminijail.c b/libminijail.c
index 5cccc32..1d8869c 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -594,7 +594,7 @@
void drop_caps(const struct minijail *j)
{
cap_t caps = cap_get_proc();
- cap_value_t raise_flag[1];
+ cap_value_t flag[1];
unsigned int i;
if (!caps)
die("can't get process caps");
@@ -605,25 +605,48 @@
if (cap_clear_flag(caps, CAP_PERMITTED))
die("can't clear permitted caps");
for (i = 0; i < sizeof(j->caps) * 8 && cap_valid((int)i); ++i) {
- if (i != CAP_SETPCAP && !(j->caps & (1 << i)))
+ /* Keep CAP_SETPCAP for dropping bounding set bits. */
+ if (i != CAP_SETPCAP && !(j->caps & (1UL << i)))
continue;
- raise_flag[0] = i;
- if (cap_set_flag(caps, CAP_EFFECTIVE, 1, raise_flag, CAP_SET))
+ flag[0] = i;
+ if (cap_set_flag(caps, CAP_EFFECTIVE, 1, flag, CAP_SET))
die("can't add effective cap");
- if (cap_set_flag(caps, CAP_PERMITTED, 1, raise_flag, CAP_SET))
+ if (cap_set_flag(caps, CAP_PERMITTED, 1, flag, CAP_SET))
die("can't add permitted cap");
- if (cap_set_flag(caps, CAP_INHERITABLE, 1, raise_flag, CAP_SET))
+ if (cap_set_flag(caps, CAP_INHERITABLE, 1, flag, CAP_SET))
die("can't add inheritable cap");
}
if (cap_set_proc(caps))
- die("can't apply cleaned capset");
- cap_free(caps);
+ die("can't apply initial cleaned capset");
+
+ /*
+ * Instead of dropping bounding set first, do it here in case
+ * the caller had a more permissive bounding set which could
+ * have been used above to raise a capability that wasn't already
+ * present. This requires CAP_SETPCAP, so we raised/kept it above.
+ */
for (i = 0; i < sizeof(j->caps) * 8 && cap_valid((int)i); ++i) {
- if (j->caps & (1 << i))
+ if (j->caps & (1UL << i))
continue;
if (prctl(PR_CAPBSET_DROP, i))
pdie("prctl(PR_CAPBSET_DROP)");
}
+
+ /* If CAP_SETPCAP wasn't specifically requested, now we remove it. */
+ if ((j->caps & (1UL << CAP_SETPCAP)) == 0) {
+ flag[0] = CAP_SETPCAP;
+ if (cap_set_flag(caps, CAP_EFFECTIVE, 1, flag, CAP_CLEAR))
+ die("can't clear effective cap");
+ if (cap_set_flag(caps, CAP_PERMITTED, 1, flag, CAP_CLEAR))
+ die("can't clear permitted cap");
+ if (cap_set_flag(caps, CAP_INHERITABLE, 1, flag, CAP_CLEAR))
+ die("can't clear inheritable cap");
+ }
+
+ if (cap_set_proc(caps))
+ die("can't apply final cleaned capset");
+
+ cap_free(caps);
}
void set_seccomp_filter(const struct minijail *j)