cgroup: also indicate cgroup delegation state in user-accessible xattr

So far we set the "trusted.delegate" xattr on cgroups where delegation
is on. This duplicates this behaviour with the "user.delegate" xattr.
This has two benefits:

1. unprivileged clients can *read* the xattr. "systemd-cgls" can thus
   show delegated cgroups as such properly, even when invoked without
   privs

2. unprivileged systemd instances can set the xattr, i.e. when systemd
   --user delegates a cgroup to further payloads.

This weakens security a tiny bit, given that code that got a cgroup
delegated can manipulate the xattr, but I think that's OK, given they
have a higher trust level regarding cgroups anyway, if they got a
subtree delegated, and access controls on the cgroup itself are still
enforced. Moreover PID 1 as the cgroup manager only sets these xattrs,
never reads them — the xattr is primarily a way to tell payloads about
the delegation, and it's strictly this one way.
This commit is contained in:
Lennart Poettering
2022-03-16 16:32:44 +01:00
parent bde2607563
commit d9bc1c3614
2 changed files with 29 additions and 15 deletions
+21 -11
View File
@@ -772,6 +772,8 @@ void cgroup_oomd_xattr_apply(Unit *u, const char *cgroup_path) {
}
static void cgroup_xattr_apply(Unit *u) {
const char *xn;
bool b;
int r;
assert(u);
@@ -788,17 +790,25 @@ static void cgroup_xattr_apply(Unit *u) {
log_unit_debug_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
}
if (unit_cgroup_delegate(u)) {
r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
"trusted.delegate",
"1", 1,
0);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set delegate flag on control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
} else {
r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "trusted.delegate");
if (r < 0 && r != -ENODATA)
log_unit_debug_errno(u, r, "Failed to remove delegate flag on control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
/* Indicate on the cgroup whether delegation is on, via an xattr. This is best-effort, as old kernels
* didn't support xattrs on cgroups at all. Later they got support for setting 'trusted.*' xattrs,
* and even later 'user.*' xattrs. We started setting this field when 'trusted.*' was added, and
* given this is now pretty much API, let's continue to support that. But also set 'user.*' as well,
* since it is readable by any user, not just CAP_SYS_ADMIN. This hence comes with slightly weaker
* security (as users who got delegated cgroups could turn it off if they like), but this shouldn't
* be a big problem given this communicates delegation state to clients, but the manager never reads
* it. */
b = unit_cgroup_delegate(u);
FOREACH_STRING(xn, "trusted.delegate", "user.delegate") {
if (b) {
r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, xn, "1", 1, 0);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set '%s' xattr on control group %s, ignoring: %m", xn, empty_to_root(u->cgroup_path));
} else {
r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, xn);
if (r < 0 && r != -ENODATA)
log_unit_debug_errno(u, r, "Failed to remove '%s' xattr flag on control group %s, ignoring: %m", xn, empty_to_root(u->cgroup_path));
}
}
cgroup_oomd_xattr_apply(u, u->cgroup_path);