Merge pull request #26557 from poettering/more-cap-utils

new helper for cap-list.[ch] + capability-util.[ch]
This commit is contained in:
Yu Watanabe 2023-02-23 10:44:53 +09:00 committed by GitHub
commit c7d941c527
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 180 additions and 1 deletions

View file

@ -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,61 @@ 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;
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;

View file

@ -16,4 +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);

View file

@ -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;
}

View file

@ -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);

View file

@ -7,7 +7,9 @@
#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"
/* verify the capability parser */
@ -99,6 +101,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;
@ -133,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);

View file

@ -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;
}