tests: test ambient capabilities.
authorIsmo Puustinen <ismo.puustinen@intel.com>
Tue, 5 Jan 2016 11:34:41 +0000 (13:34 +0200)
committerIsmo Puustinen <ismo.puustinen@intel.com>
Tue, 12 Jan 2016 10:14:50 +0000 (12:14 +0200)
The ambient capability tests are only run if the kernel has support for
ambient capabilities.

src/test/test-capability.c
src/test/test-execute.c
test/test-execute/exec-capabilityambientset-merge.service [new file with mode: 0644]
test/test-execute/exec-capabilityambientset.service [new file with mode: 0644]

index fc8d3ff..629bb63 100644 (file)
@@ -20,6 +20,7 @@
 #include <netinet/in.h>
 #include <pwd.h>
 #include <sys/capability.h>
+#include <sys/prctl.h>
 #include <sys/socket.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -66,8 +67,9 @@ static void show_capabilities(void) {
         cap_free(text);
 }
 
-static int setup_tests(void) {
+static int setup_tests(bool *run_ambient) {
         struct passwd *nobody;
+        int r;
 
         nobody = getpwnam("nobody");
         if (!nobody) {
@@ -77,6 +79,18 @@ static int setup_tests(void) {
         test_uid = nobody->pw_uid;
         test_gid = nobody->pw_gid;
 
+        *run_ambient = false;
+
+        r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
+
+        /* There's support for PR_CAP_AMBIENT if the prctl() call
+         * succeeded or error code was something else than EINVAL. The
+         * EINVAL check should be good enough to rule out false
+         * positives. */
+
+        if (r >= 0 || errno != EINVAL)
+                *run_ambient = true;
+
         return 0;
 }
 
@@ -140,8 +154,53 @@ static void test_have_effective_cap(void) {
         assert_se(!have_effective_cap(CAP_CHOWN));
 }
 
+static void test_update_inherited_set(void) {
+        cap_t caps;
+        uint64_t set = 0;
+        cap_flag_value_t fv;
+
+        caps = cap_get_proc();
+        assert_se(caps);
+        assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
+        assert(fv == CAP_CLEAR);
+
+        set = (UINT64_C(1) << CAP_CHOWN);
+
+        assert_se(!capability_update_inherited_set(caps, set));
+        assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
+        assert(fv == CAP_SET);
+
+        cap_free(caps);
+}
+
+static void test_set_ambient_caps(void) {
+        cap_t caps;
+        uint64_t set = 0;
+        cap_flag_value_t fv;
+
+        caps = cap_get_proc();
+        assert_se(caps);
+        assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
+        assert(fv == CAP_CLEAR);
+        cap_free(caps);
+
+        assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 0);
+
+        set = (UINT64_C(1) << CAP_CHOWN);
+
+        assert_se(!capability_ambient_set_apply(set, true));
+
+        caps = cap_get_proc();
+        assert_se(!cap_get_flag(caps, CAP_CHOWN, CAP_INHERITABLE, &fv));
+        assert(fv == CAP_SET);
+        cap_free(caps);
+
+        assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 1);
+}
+
 int main(int argc, char *argv[]) {
         int r;
+        bool run_ambient;
 
         log_parse_environment();
         log_open();
@@ -149,14 +208,19 @@ int main(int argc, char *argv[]) {
         if (getuid() != 0)
                 return EXIT_TEST_SKIP;
 
-        r = setup_tests();
+        r = setup_tests(&run_ambient);
         if (r < 0)
                 return -r;
 
         show_capabilities();
 
         test_drop_privileges();
+        test_update_inherited_set();
+
         fork_test(test_have_effective_cap);
 
+        if (run_ambient)
+                fork_test(test_set_ambient_caps);
+
         return 0;
 }
index 753afad..92857cb 100644 (file)
@@ -20,6 +20,7 @@
 #include <grp.h>
 #include <pwd.h>
 #include <stdio.h>
+#include <sys/prctl.h>
 #include <sys/types.h>
 
 #include "fileio.h"
@@ -224,6 +225,20 @@ static void test_exec_capabilityboundingset(Manager *m) {
         test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED);
 }
 
+static void test_exec_capabilityambientset(Manager *m) {
+        int r;
+
+        /* Check if the kernel has support for ambient capabilities. Run
+         * the tests only if that's the case. Clearing all ambient
+         * capabilities is fine, since we are expecting them to be unset
+         * in the first place for the tests. */
+        r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
+        if (r >= 0 || errno != EINVAL) {
+                test(m, "exec-capabilityambientset.service", 0, CLD_EXITED);
+                test(m, "exec-capabilityambientset-merge.service", 0, CLD_EXITED);
+        }
+}
+
 static void test_exec_privatenetwork(Manager *m) {
         int r;
 
@@ -266,6 +281,7 @@ int main(int argc, char *argv[]) {
                 test_exec_umask,
                 test_exec_runtimedirectory,
                 test_exec_capabilityboundingset,
+                test_exec_capabilityambientset,
                 test_exec_oomscoreadjust,
                 test_exec_ioschedulingclass,
                 NULL,
diff --git a/test/test-execute/exec-capabilityambientset-merge.service b/test/test-execute/exec-capabilityambientset-merge.service
new file mode 100644 (file)
index 0000000..6496438
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=Test for AmbientCapabilities
+
+[Service]
+ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000003000"'
+Type=oneshot
+User=nobody
+AmbientCapabilities=CAP_NET_ADMIN
+AmbientCapabilities=CAP_NET_RAW
diff --git a/test/test-execute/exec-capabilityambientset.service b/test/test-execute/exec-capabilityambientset.service
new file mode 100644 (file)
index 0000000..d63f884
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Test for AmbientCapabilities
+
+[Service]
+ExecStart=/bin/sh -x -c 'c=$$(grep "CapAmb:" /proc/self/status); test "$$c" = "CapAmb: 0000000000003000"'
+Type=oneshot
+User=nobody
+AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW