From 8cf4674d8673f692e548361dc4352e02244862ed Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 20 Feb 2023 11:46:08 +0100 Subject: [PATCH 1/3] cap-list: add capability_set_to_strv() --- src/basic/cap-list.c | 25 +++++++++++++++++++++++++ src/basic/cap-list.h | 1 + src/test/test-cap-list.c | 18 ++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c index 45a5b371e84..b95b9b140ee 100644 --- a/src/basic/cap-list.c +++ b/src/basic/cap-list.c @@ -11,6 +11,7 @@ #include "parse-util.h" #include "stdio-util.h" #include "string-util.h" +#include "strv.h" static const struct capability_name* lookup_capability(register const char *str, register GPERF_LEN_TYPE len); @@ -102,6 +103,30 @@ int capability_set_to_string(uint64_t set, char **ret) { return 0; } +int capability_set_to_strv(uint64_t set, char ***ret) { + _cleanup_strv_free_ char **l = NULL; + int r; + + assert(ret); + + for (unsigned i = 0; i <= cap_last_cap(); i++) { + const char *p; + + if (!FLAGS_SET(set, UINT64_C(1) << i)) + continue; + + p = CAPABILITY_TO_STRING(i); + assert(p); + + r = strv_extend(&l, p); + if (r < 0) + return r; + } + + *ret = TAKE_PTR(l); + return 0; +} + int capability_set_from_string(const char *s, uint64_t *ret) { uint64_t val = 0; bool good = true; diff --git a/src/basic/cap-list.h b/src/basic/cap-list.h index bddadcbae5f..fdf4366de0e 100644 --- a/src/basic/cap-list.h +++ b/src/basic/cap-list.h @@ -16,4 +16,5 @@ int capability_from_name(const char *name); int capability_list_length(void); int capability_set_to_string(uint64_t set, char **ret); +int capability_set_to_strv(uint64_t set, char ***ret); int capability_set_from_string(const char *s, uint64_t *ret); diff --git a/src/test/test-cap-list.c b/src/test/test-cap-list.c index 8df425bdd29..42503d294c6 100644 --- a/src/test/test-cap-list.c +++ b/src/test/test-cap-list.c @@ -8,6 +8,7 @@ #include "capability-util.h" #include "parse-util.h" #include "string-util.h" +#include "strv.h" #include "tests.h" /* verify the capability parser */ @@ -99,6 +100,23 @@ TEST(capability_set_from_string) { assert_se(c == (UINT64_C(1) << 4) - 1); } +static void test_capability_set_to_strv_one(uint64_t m, char **l) { + _cleanup_strv_free_ char **b = NULL; + + assert_se(capability_set_to_strv(m, &b) >= 0); + assert_se(strv_equal(l, b)); +} + +TEST(capability_set_to_strv) { + test_capability_set_to_strv_one(0, STRV_MAKE(NULL)); + test_capability_set_to_strv_one(UINT64_C(1) << CAP_MKNOD, STRV_MAKE("cap_mknod")); + test_capability_set_to_strv_one((UINT64_C(1) << CAP_MKNOD) | + (UINT64_C(1) << CAP_NET_BIND_SERVICE), STRV_MAKE("cap_net_bind_service", "cap_mknod")); + test_capability_set_to_strv_one((UINT64_C(1) << CAP_MKNOD) | + (UINT64_C(1) << CAP_NET_BIND_SERVICE) | + (UINT64_C(1) << CAP_IPC_OWNER), STRV_MAKE("cap_net_bind_service", "cap_ipc_owner", "cap_mknod")); +} + static void test_capability_set_to_string_invalid(uint64_t invalid_cap_set) { uint64_t c; From 66c1e440c70345751181f335210eaf8195e958d5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Feb 2023 13:05:07 +0100 Subject: [PATCH 2/3] cap-list: add capability_set_to_string_negative() --- src/basic/cap-list.c | 31 +++++++++++++++++++++++++++++++ src/basic/cap-list.h | 1 + src/test/test-cap-list.c | 23 +++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c index b95b9b140ee..80d48c142e3 100644 --- a/src/basic/cap-list.c +++ b/src/basic/cap-list.c @@ -103,6 +103,37 @@ int capability_set_to_string(uint64_t set, char **ret) { return 0; } +int capability_set_to_string_negative(uint64_t set, char **ret) { + _cleanup_free_ char *a = NULL, *b = NULL; + int r; + + assert(ret); + + /* Format the specified capability mask both in positive way (i.e. just listing caps) and in negative + * way (i.e. listing only caps that are missing from the full set) and return the shorter version of + * the two. */ + + r = capability_set_to_string(set, &a); + if (r < 0) + return r; + + r = capability_set_to_string(~set & all_capabilities(), &b); + if (r < 0) + return r; + + if (strlen(a) <= 1 + strlen(b)) + *ret = TAKE_PTR(a); + else { + char *c = strjoin("~", b); + if (!c) + return -ENOMEM; + + *ret = c; + } + + return 0; +} + int capability_set_to_strv(uint64_t set, char ***ret) { _cleanup_strv_free_ char **l = NULL; int r; diff --git a/src/basic/cap-list.h b/src/basic/cap-list.h index fdf4366de0e..3028197b56a 100644 --- a/src/basic/cap-list.h +++ b/src/basic/cap-list.h @@ -16,5 +16,6 @@ int capability_from_name(const char *name); int capability_list_length(void); int capability_set_to_string(uint64_t set, char **ret); +int capability_set_to_string_negative(uint64_t set, char **ret); int capability_set_to_strv(uint64_t set, char ***ret); int capability_set_from_string(const char *s, uint64_t *ret); diff --git a/src/test/test-cap-list.c b/src/test/test-cap-list.c index 42503d294c6..a9cbf695b90 100644 --- a/src/test/test-cap-list.c +++ b/src/test/test-cap-list.c @@ -7,6 +7,7 @@ #include "cap-list.h" #include "capability-util.h" #include "parse-util.h" +#include "random-util.h" #include "string-util.h" #include "strv.h" #include "tests.h" @@ -151,4 +152,26 @@ TEST(capability_set_to_string) { test_capability_set_to_string_invalid(all_capabilities() + 1); } +TEST(capability_set_to_string_negative) { + + for (unsigned i = 0; i < 150; i++) { + _cleanup_free_ char *a = NULL, *b = NULL; + + uint64_t m = + random_u64() % (UINT64_C(1) << (cap_last_cap() + 1)); + + assert_se(capability_set_to_string(m, &a) >= 0); + assert_se(capability_set_to_string_negative(m, &b) >= 0); + + printf("%s (%zu) → ", a, strlen(a)); + + if (streq(a, b)) + printf("same\n"); + else + printf("%s (%zu)\n", b, strlen(b)); + + assert_se(strlen(b) <= strlen(a)); + } +} + DEFINE_TEST_MAIN(LOG_INFO); From a649419aae0f24990186eaec5a11bbc169ff8c7d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 22 Feb 2023 13:05:44 +0100 Subject: [PATCH 3/3] capability-util: add new capability_get_ambient() helper --- src/basic/capability-util.c | 25 +++++++++++++++++ src/basic/capability-util.h | 2 ++ src/test/test-capability.c | 55 ++++++++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index bda7cbf45c5..87fb2203f08 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -605,3 +605,28 @@ int capability_quintet_enforce(const CapabilityQuintet *q) { return 0; } + +int capability_get_ambient(uint64_t *ret) { + uint64_t a = 0; + int r; + + assert(ret); + + if (!ambient_capabilities_supported()) { + *ret = 0; + return 0; + } + + for (unsigned i = 0; i <= cap_last_cap(); i++) { + r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, i, 0, 0); + if (r < 0) + return -errno; + + if (r) + a |= UINT64_C(1) << i; + } + + + *ret = a; + return 1; +} diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h index 07b13862c79..3e0f901a3d3 100644 --- a/src/basic/capability-util.h +++ b/src/basic/capability-util.h @@ -83,3 +83,5 @@ static inline bool capability_quintet_is_set(const CapabilityQuintet *q) { bool capability_quintet_mangle(CapabilityQuintet *q); int capability_quintet_enforce(const CapabilityQuintet *q); + +int capability_get_ambient(uint64_t *ret); diff --git a/src/test/test-capability.c b/src/test/test-capability.c index dbb258c7d26..e989065f492 100644 --- a/src/test/test-capability.c +++ b/src/test/test-capability.c @@ -17,6 +17,7 @@ #include "macro.h" #include "missing_prctl.h" #include "parse-util.h" +#include "process-util.h" #include "string-util.h" #include "tests.h" @@ -247,10 +248,60 @@ static void test_ensure_cap_64bit(void) { assert_cc(CAP_LAST_CAP <= 63); } +static void test_capability_get_ambient(void) { + uint64_t c; + int r; + + assert_se(capability_get_ambient(&c) >= 0); + + r = safe_fork("(getambient)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL); + assert_se(r >= 0); + + if (r == 0) { + int x, y; + /* child */ + assert_se(capability_get_ambient(&c) >= 0); + + x = capability_ambient_set_apply( + (UINT64_C(1) << CAP_MKNOD)| + (UINT64_C(1) << CAP_LINUX_IMMUTABLE), + /* also_inherit= */ true); + assert_se(x >= 0 || ERRNO_IS_PRIVILEGE(x)); + + assert_se(capability_get_ambient(&c) >= 0); + assert_se(x < 0 || FLAGS_SET(c, UINT64_C(1) << CAP_MKNOD)); + assert_se(x < 0 || FLAGS_SET(c, UINT64_C(1) << CAP_LINUX_IMMUTABLE)); + assert_se(x < 0 || !FLAGS_SET(c, UINT64_C(1) << CAP_SETPCAP)); + + y = capability_bounding_set_drop( + ((UINT64_C(1) << CAP_LINUX_IMMUTABLE)| + (UINT64_C(1) << CAP_SETPCAP)), + /* right_now= */ true); + assert_se(y >= 0 || ERRNO_IS_PRIVILEGE(y)); + + assert_se(capability_get_ambient(&c) >= 0); + assert_se(x < 0 || y < 0 || !FLAGS_SET(c, UINT64_C(1) << CAP_MKNOD)); + assert_se(x < 0 || y < 0 || FLAGS_SET(c, UINT64_C(1) << CAP_LINUX_IMMUTABLE)); + assert_se(x < 0 || y < 0 || !FLAGS_SET(c, UINT64_C(1) << CAP_SETPCAP)); + + y = capability_bounding_set_drop( + (UINT64_C(1) << CAP_SETPCAP), + /* right_now= */ true); + assert_se(y >= 0 || ERRNO_IS_PRIVILEGE(y)); + + assert_se(capability_get_ambient(&c) >= 0); + assert_se(x < 0 || y < 0 || !FLAGS_SET(c, UINT64_C(1) << CAP_MKNOD)); + assert_se(x < 0 || y < 0 || !FLAGS_SET(c, UINT64_C(1) << CAP_LINUX_IMMUTABLE)); + assert_se(x < 0 || y < 0 || !FLAGS_SET(c, UINT64_C(1) << CAP_SETPCAP)); + + _exit(EXIT_SUCCESS); + } +} + int main(int argc, char *argv[]) { bool run_ambient; - test_setup_logging(LOG_INFO); + test_setup_logging(LOG_DEBUG); test_ensure_cap_64bit(); @@ -275,5 +326,7 @@ int main(int argc, char *argv[]) { if (run_ambient) fork_test(test_apply_ambient_caps); + test_capability_get_ambient(); + return 0; }