mirror of
https://github.com/systemd/systemd.git
synced 2026-08-09 09:32:04 +00:00
homed: verify privileged worker changes
Workers may return a newer embedded identity while processing an owner update. Accept self-modifiable changes, but require privileged changes to carry a trusted signature. Follow-up for70a5db5822(cherry picked from commit765dc96746) (cherry picked from commita7bce5b205) (cherry picked from commitb7addfc2f7) (cherry picked from commit7de1a48462)
This commit is contained in:
@@ -1003,6 +1003,60 @@ static void home_change_finish(Home *h, int ret, UserRecord *hr) {
|
||||
}
|
||||
|
||||
if (hr) {
|
||||
_cleanup_(user_record_unrefp) UserRecord *signed_hr = NULL;
|
||||
bool signed_locally = false;
|
||||
int allowed;
|
||||
|
||||
allowed = user_record_self_changes_allowed(h->record, hr);
|
||||
if (allowed < 0) {
|
||||
r = log_error_errno(allowed, "Failed to determine whether worker returned permitted changes: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!allowed) {
|
||||
r = home_verify_user_record(h, hr, &signed_locally, &error);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
} else {
|
||||
int is_signed = manager_verify_user_record(h->manager, hr);
|
||||
|
||||
switch (is_signed) {
|
||||
|
||||
case USER_RECORD_SIGNED_EXCLUSIVE:
|
||||
signed_locally = true;
|
||||
break;
|
||||
|
||||
case USER_RECORD_SIGNED:
|
||||
case USER_RECORD_FOREIGN:
|
||||
break;
|
||||
|
||||
case USER_RECORD_UNSIGNED:
|
||||
if (h->signed_locally <= 0) {
|
||||
r = sd_bus_error_setf(&error, BUS_ERROR_HOME_RECORD_SIGNED,
|
||||
"Home %s is signed and cannot be modified locally.", h->user_name);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = manager_sign_user_record(h->manager, hr, &signed_hr, &error);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
hr = signed_hr;
|
||||
signed_locally = true;
|
||||
break;
|
||||
|
||||
case -ENOKEY:
|
||||
r = home_verify_user_record(h, hr, &signed_locally, &error);
|
||||
assert(r < 0);
|
||||
goto finish;
|
||||
|
||||
default:
|
||||
assert(is_signed < 0);
|
||||
r = log_error_errno(is_signed, "Failed to verify worker returned record: %m");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(flags, SD_HOMED_UPDATE_OFFLINE)) {
|
||||
r = user_record_good_authentication(h->record);
|
||||
if (r < 0)
|
||||
@@ -1010,8 +1064,11 @@ static void home_change_finish(Home *h, int ret, UserRecord *hr) {
|
||||
}
|
||||
|
||||
r = home_set_record(h, hr);
|
||||
if (r >= 0)
|
||||
if (r >= 0) {
|
||||
h->signed_locally = signed_locally;
|
||||
|
||||
r = home_save_record(h);
|
||||
}
|
||||
if (r < 0) {
|
||||
if (FLAGS_SET(flags, SD_HOMED_UPDATE_OFFLINE)) {
|
||||
log_error_errno(r, "Failed to update home record and write it to disk: %m");
|
||||
|
||||
@@ -848,17 +848,61 @@ homectl inspect matchtest
|
||||
homectl inspect matchtest | grep "Area: quux3"
|
||||
homectl remove matchtest
|
||||
|
||||
NEWPASSWORD=foobar homectl create idgrouptest --enforce-password-policy=no
|
||||
NEWPASSWORD=foobar homectl create idgrouptest --storage=directory --shell=/bin/bash --enforce-password-policy=no --rebalance-weight=off
|
||||
PASSWORD=foobar homectl activate idgrouptest
|
||||
machinectl shell idgrouptest@ /usr/bin/bash -euxo pipefail -c "jq '.memberOf = ((.memberOf // []) + [\"systemd-journal\"] | unique) | .lastChangeUSec = ((.lastChangeUSec // 0) + 3600000000)' /home/idgrouptest/.identity > /home/idgrouptest/.identity.new && mv -f /home/idgrouptest/.identity.new /home/idgrouptest/.identity"
|
||||
jq -e '.memberOf | index("systemd-journal") != null' /home/idgrouptest/.identity
|
||||
PASSWORD=foobar homectl authenticate idgrouptest
|
||||
groups="$(machinectl shell idgrouptest@ /usr/bin/groups)"
|
||||
(! grep -q systemd-journal <<<"$groups")
|
||||
(! grep systemd-journal <<<"$groups" >/dev/null)
|
||||
homectl deactivate idgrouptest ||:
|
||||
wait_for_state idgrouptest inactive
|
||||
homectl remove idgrouptest
|
||||
|
||||
# Install a PK rule that allows 'idgrouptest2' user to update homed even
|
||||
# though they are not on an fg console, just for testing
|
||||
mkdir -p /etc/polkit-1/rules.d
|
||||
cat >/etc/polkit-1/rules.d/updatehome.rules <<'EOF'
|
||||
polkit.addRule(function(action, subject) {
|
||||
if (action.id == "org.freedesktop.home1.update-home-by-owner" &&
|
||||
subject.user == "idgrouptest2") {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
EOF
|
||||
trap 'rm -f /etc/polkit-1/rules.d/updatehome.rules' RETURN ERR EXIT
|
||||
systemctl try-reload-or-restart polkit.service
|
||||
NEWPASSWORD=foobar homectl create idgrouptest2 --storage=directory --shell=/bin/bash --enforce-password-policy=no --rebalance-weight=off
|
||||
PASSWORD=foobar homectl activate idgrouptest2
|
||||
cat >/tmp/idgrouptest-add-group.sh <<'EOF'
|
||||
#!/bin/bash
|
||||
set -exuo pipefail
|
||||
|
||||
loginctl show-session "${XDG_SESSION_ID:?}" -p Active --value | grep '^yes$' >/dev/null
|
||||
RECORD="$(busctl -j call org.freedesktop.home1 /org/freedesktop/home1 org.freedesktop.home1.Manager GetUserRecordByName s "$USER" | jq -r '.data[0]')"
|
||||
TS="$(printf '%s' "$RECORD" | jq '.lastChangeUSec')"
|
||||
NEW_TS=$((TS + 172800000000))
|
||||
jq --arg g systemd-journal --argjson ts "$NEW_TS" \
|
||||
'.memberOf = ((.memberOf // []) + [$g]) | .lastChangeUSec = $ts' \
|
||||
~/.identity > ~/.identity.new
|
||||
mv -f ~/.identity.new ~/.identity
|
||||
# Ensure the identity update is persisted before UpdateHomeEx reads it.
|
||||
sync
|
||||
|
||||
UPDATE_TS=$((TS + 1))
|
||||
UPDATE_RECORD="$(printf '%s' "$RECORD" | jq -c --argjson ts "$UPDATE_TS" --arg p foobar 'del(.binding, .status, .signature) | .lastChangeUSec = $ts | .secret = {password: [$p]}')"
|
||||
(! busctl call org.freedesktop.home1 /org/freedesktop/home1 org.freedesktop.home1.Manager UpdateHomeEx "sa{sh}t" "$UPDATE_RECORD" 0 0 )
|
||||
EOF
|
||||
chmod +x /tmp/idgrouptest-add-group.sh
|
||||
machinectl shell idgrouptest2@ /tmp/idgrouptest-add-group.sh
|
||||
rm -f /tmp/idgrouptest-add-group.sh
|
||||
PASSWORD=foobar homectl authenticate idgrouptest2
|
||||
groups="$(machinectl shell idgrouptest2@ /usr/bin/groups)"
|
||||
(! grep systemd-journal <<<"$groups" >/dev/null)
|
||||
homectl deactivate idgrouptest2 ||:
|
||||
wait_for_state idgrouptest2 inactive
|
||||
homectl remove idgrouptest2
|
||||
|
||||
systemd-analyze log-level info
|
||||
|
||||
touch /testok
|
||||
|
||||
Reference in New Issue
Block a user