diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index 8040eb399a..f4fab22e73 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -8,8 +8,9 @@ #include #include "alloc-util.h" -#include "capability-util.h" #include "cap-list.h" +#include "capability-util.h" +#include "fd-util.h" #include "fileio.h" #include "log.h" #include "logarithm.h" @@ -17,6 +18,8 @@ #include "missing_prctl.h" #include "missing_threads.h" #include "parse-util.h" +#include "pidref.h" +#include "stat-util.h" #include "user-util.h" int have_effective_cap(int value) { @@ -607,3 +610,78 @@ int capability_get_ambient(uint64_t *ret) { *ret = a; return 1; } + +int pidref_get_capability(const PidRef *pidref, CapabilityQuintet *ret) { + int r; + + if (!pidref_is_set(pidref)) + return -ESRCH; + if (pidref_is_remote(pidref)) + return -EREMOTE; + + const char *path = procfs_file_alloca(pidref->pid, "status"); + _cleanup_fclose_ FILE *f = fopen(path, "re"); + if (!f) { + if (errno == ENOENT && proc_mounted() == 0) + return -ENOSYS; + + return -errno; + } + + CapabilityQuintet q = CAPABILITY_QUINTET_NULL; + for (;;) { + _cleanup_free_ char *line = NULL; + + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) + break; + + static const struct { + const char *field; + size_t offset; + } fields[] = { + { "CapBnd:", offsetof(CapabilityQuintet, bounding) }, + { "CapInh:", offsetof(CapabilityQuintet, inheritable) }, + { "CapPrm:", offsetof(CapabilityQuintet, permitted) }, + { "CapEff:", offsetof(CapabilityQuintet, effective) }, + { "CapAmb:", offsetof(CapabilityQuintet, ambient) }, + }; + + FOREACH_ELEMENT(i, fields) { + + const char *p = first_word(line, i->field); + if (!p) + continue; + + uint64_t *v = (uint64_t*) ((uint8_t*) &q + i->offset); + + if (*v != CAP_MASK_UNSET) + return -EBADMSG; + + r = safe_atoux64(p, v); + if (r < 0) + return r; + + if (*v == CAP_MASK_UNSET) + return -EBADMSG; + } + } + + if (q.effective == CAP_MASK_UNSET || + q.inheritable == CAP_MASK_UNSET || + q.permitted == CAP_MASK_UNSET || + q.effective == CAP_MASK_UNSET || + q.ambient == CAP_MASK_UNSET) + return -EBADMSG; + + r = pidref_verify(pidref); + if (r < 0) + return r; + + if (ret) + *ret = q; + + return 0; +} diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h index bb781940c4..9caae1ac06 100644 --- a/src/basic/capability-util.h +++ b/src/basic/capability-util.h @@ -8,6 +8,7 @@ #include "macro.h" #include "missing_capability.h" +#include "pidref.h" /* Special marker used when storing a capabilities mask as "unset" */ #define CAP_MASK_UNSET UINT64_MAX @@ -66,14 +67,18 @@ typedef struct CapabilityQuintet { assert_cc(CAP_LAST_CAP < 64); -#define CAPABILITY_QUINTET_NULL { CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET } +#define CAPABILITY_QUINTET_NULL (CapabilityQuintet) { CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET, CAP_MASK_UNSET } + +static inline bool capability_is_set(uint64_t v) { + return v != CAP_MASK_UNSET; +} static inline bool capability_quintet_is_set(const CapabilityQuintet *q) { - return q->effective != CAP_MASK_UNSET || - q->bounding != CAP_MASK_UNSET || - q->inheritable != CAP_MASK_UNSET || - q->permitted != CAP_MASK_UNSET || - q->ambient != CAP_MASK_UNSET; + return capability_is_set(q->effective) || + capability_is_set(q->bounding) || + capability_is_set(q->inheritable) || + capability_is_set(q->permitted) || + capability_is_set(q->ambient); } /* Mangles the specified caps quintet taking the current bounding set into account: @@ -84,3 +89,5 @@ bool capability_quintet_mangle(CapabilityQuintet *q); int capability_quintet_enforce(const CapabilityQuintet *q); int capability_get_ambient(uint64_t *ret); + +int pidref_get_capability(const PidRef *pidref, CapabilityQuintet *ret); diff --git a/src/basic/process-util.c b/src/basic/process-util.c index c74a7e7ea4..d02a122a35 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -500,22 +500,6 @@ int pidref_is_kernel_thread(const PidRef *pid) { return result; } -int get_process_capeff(pid_t pid, char **ret) { - const char *p; - int r; - - assert(pid >= 0); - assert(ret); - - p = procfs_file_alloca(pid, "status"); - - r = get_proc_field(p, "CapEff", WHITESPACE, ret); - if (r == -ENOENT) - return -ESRCH; - - return r; -} - static int get_process_link_contents(pid_t pid, const char *proc_file, char **ret) { const char *p; int r; diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 0763b64cff..f1088afc1d 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -50,7 +50,6 @@ int get_process_exe(pid_t pid, char **ret); int pid_get_uid(pid_t pid, uid_t *ret); int pidref_get_uid(const PidRef *pid, uid_t *ret); int get_process_gid(pid_t pid, gid_t *ret); -int get_process_capeff(pid_t pid, char **ret); int get_process_cwd(pid_t pid, char **ret); int get_process_root(pid_t pid, char **ret); int get_process_environ(pid_t pid, char **ret); diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c index c48ad81f37..50dc8867e1 100644 --- a/src/journal/journald-context.c +++ b/src/journal/journald-context.c @@ -132,6 +132,7 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) { .log_level_max = -1, .log_ratelimit_interval = s->ratelimit_interval, .log_ratelimit_burst = s->ratelimit_burst, + .capability_quintet = CAPABILITY_QUINTET_NULL, }; r = hashmap_ensure_put(&s->client_contexts, NULL, PID_TO_PTR(pid), c); @@ -154,7 +155,6 @@ static void client_context_reset(Server *s, ClientContext *c) { c->comm = mfree(c->comm); c->exe = mfree(c->exe); c->cmdline = mfree(c->cmdline); - c->capeff = mfree(c->capeff); c->auditid = AUDIT_SESSION_INVALID; c->loginuid = UID_INVALID; @@ -184,6 +184,8 @@ static void client_context_reset(Server *s, ClientContext *c) { c->log_filter_allowed_patterns = set_free_free(c->log_filter_allowed_patterns); c->log_filter_denied_patterns = set_free_free(c->log_filter_denied_patterns); + + c->capability_quintet = CAPABILITY_QUINTET_NULL; } static ClientContext* client_context_free(Server *s, ClientContext *c) { @@ -233,8 +235,7 @@ static void client_context_read_basic(ClientContext *c) { if (pid_get_cmdline(c->pid, SIZE_MAX, PROCESS_CMDLINE_QUOTE, &t) >= 0) free_and_replace(c->cmdline, t); - if (get_process_capeff(c->pid, &t) >= 0) - free_and_replace(c->capeff, t); + (void) pidref_get_capability(&PIDREF_MAKE_FROM_PID(c->pid), &c->capability_quintet); } static int client_context_read_label( diff --git a/src/journal/journald-context.h b/src/journal/journald-context.h index 6e0d9f6f75..34637f85e4 100644 --- a/src/journal/journald-context.h +++ b/src/journal/journald-context.h @@ -7,6 +7,7 @@ #include "sd-id128.h" +#include "capability-util.h" #include "set.h" #include "time-util.h" @@ -27,7 +28,7 @@ struct ClientContext { char *comm; char *exe; char *cmdline; - char *capeff; + CapabilityQuintet capability_quintet; uint32_t auditid; uid_t loginuid; diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index bebc1e584c..8fcfa0c57b 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -1109,7 +1109,7 @@ static void server_dispatch_message_real( * Let's use a heap allocation for this one. */ cmdline1 = set_iovec_string_field(iovec, &n, "_CMDLINE=", c->cmdline); - IOVEC_ADD_STRING_FIELD(iovec, n, c->capeff, "_CAP_EFFECTIVE"); /* Read from /proc/.../status */ + IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->capability_quintet.effective, uint64_t, capability_is_set, "%" PRIx64, "_CAP_EFFECTIVE"); IOVEC_ADD_SIZED_FIELD(iovec, n, c->label, c->label_size, "_SELINUX_CONTEXT"); IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "_AUDIT_SESSION"); IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->loginuid, uid_t, uid_is_valid, UID_FMT, "_AUDIT_LOGINUID"); @@ -1144,7 +1144,7 @@ static void server_dispatch_message_real( if (o->cmdline) cmdline2 = set_iovec_string_field(iovec, &n, "OBJECT_CMDLINE=", o->cmdline); - IOVEC_ADD_STRING_FIELD(iovec, n, o->capeff, "OBJECT_CAP_EFFECTIVE"); + IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->capability_quintet.effective, uint64_t, capability_is_set, "%" PRIx64, "OBJECT_CAP_EFFECTIVE"); IOVEC_ADD_SIZED_FIELD(iovec, n, o->label, o->label_size, "OBJECT_SELINUX_CONTEXT"); IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "OBJECT_AUDIT_SESSION"); IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->loginuid, uid_t, uid_is_valid, UID_FMT, "OBJECT_AUDIT_LOGINUID"); diff --git a/src/shared/condition.c b/src/shared/condition.c index ac23681a11..e4aa50c8f7 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -21,6 +21,7 @@ #include "battery-util.h" #include "blockdev-util.h" #include "cap-list.h" +#include "capability-util.h" #include "cgroup-util.h" #include "compare-operator.h" #include "condition.h" @@ -701,45 +702,23 @@ static int condition_test_security(Condition *c, char **env) { } static int condition_test_capability(Condition *c, char **env) { - unsigned long long capabilities = (unsigned long long) -1; - _cleanup_fclose_ FILE *f = NULL; - int value, r; + int r; assert(c); assert(c->parameter); assert(c->type == CONDITION_CAPABILITY); /* If it's an invalid capability, we don't have it */ - value = capability_from_name(c->parameter); + int value = capability_from_name(c->parameter); if (value < 0) return -EINVAL; - /* If it's a valid capability we default to assume - * that we have it */ + CapabilityQuintet q; + r = pidref_get_capability(&PIDREF_MAKE_FROM_PID(getpid_cached()), &q); + if (r < 0) + return r; - f = fopen("/proc/self/status", "re"); - if (!f) - return -errno; - - for (;;) { - _cleanup_free_ char *line = NULL; - - r = read_line(f, LONG_LINE_MAX, &line); - if (r < 0) - return r; - if (r == 0) - break; - - const char *p = startswith(line, "CapBnd:"); - if (p) { - if (sscanf(p, "%llx", &capabilities) != 1) - return -EIO; - - break; - } - } - - return !!(capabilities & (1ULL << value)); + return !!(q.bounding & ((UINT64_C(1) << value))); } static int condition_test_needs_update(Condition *c, char **env) { diff --git a/src/test/test-capability.c b/src/test/test-capability.c index e497cccead..6c23c1bb28 100644 --- a/src/test/test-capability.c +++ b/src/test/test-capability.c @@ -305,6 +305,18 @@ static void test_capability_get_ambient(void) { } } +static void test_pidref_get_capability(void) { + CapabilityQuintet q = CAPABILITY_QUINTET_NULL; + + assert_se(pidref_get_capability(&PIDREF_MAKE_FROM_PID(getpid_cached()), &q) >= 0); + + assert_se(q.effective != CAP_MASK_UNSET); + assert_se(q.inheritable != CAP_MASK_UNSET); + assert_se(q.permitted != CAP_MASK_UNSET); + assert_se(q.effective != CAP_MASK_UNSET); + assert_se(q.ambient != CAP_MASK_UNSET); +} + int main(int argc, char *argv[]) { bool run_ambient; @@ -336,5 +348,7 @@ int main(int argc, char *argv[]) { test_capability_get_ambient(); + test_pidref_get_capability(); + return 0; } diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 76f5598325..60b568b2d2 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -363,24 +363,6 @@ TEST(status_field) { } } -TEST(capeff) { - for (int pid = 0; pid < 2; pid++) { - _cleanup_free_ char *capeff = NULL; - int r, p; - - r = get_process_capeff(0, &capeff); - log_info("capeff: '%s' (r=%d)", capeff, r); - - if (IN_SET(r, -ENOENT, -EPERM)) - return; - - assert_se(r == 0); - assert_se(*capeff); - p = capeff[strspn(capeff, HEXDIGITS)]; - assert_se(!p || isspace(p)); - } -} - TEST(read_one_line_file) { _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-fileio-1lf-XXXXXX"; int fd;