mirror of
https://github.com/systemd/systemd.git
synced 2025-01-03 05:18:09 +03:00
capability-util: generalize helper to acquire local caps (#35403)
This generalizes and modernizes the code to acquire set of local caps, based on the code for this in the condition logic. Uses PidRef, and acquires the full quintuplet of caps. This can be considered preparation to one day maybe build without libcap.
This commit is contained in:
commit
8a135111ca
@ -8,8 +8,9 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user