logind: automatically pull in systemd-pcrlogin@.service on first login
This commit is contained in:
@@ -4321,7 +4321,7 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err
|
|||||||
/* If the user is stopping, we're tracking stop jobs here. So don't send reply. */
|
/* If the user is stopping, we're tracking stop jobs here. So don't send reply. */
|
||||||
if (!user->stopping) {
|
if (!user->stopping) {
|
||||||
char **user_job;
|
char **user_job;
|
||||||
FOREACH_ARGUMENT(user_job, &user->runtime_dir_job, &user->service_manager_job)
|
FOREACH_ARGUMENT(user_job, &user->runtime_dir_job, &user->service_manager_job, &user->measure_job)
|
||||||
if (streq_ptr(path, *user_job)) {
|
if (streq_ptr(path, *user_job)) {
|
||||||
*user_job = mfree(*user_job);
|
*user_job = mfree(*user_job);
|
||||||
|
|
||||||
|
|||||||
+60
-2
@@ -10,6 +10,7 @@
|
|||||||
#include "bus-locator.h"
|
#include "bus-locator.h"
|
||||||
#include "bus-util.h"
|
#include "bus-util.h"
|
||||||
#include "clean-ipc.h"
|
#include "clean-ipc.h"
|
||||||
|
#include "efi-loader.h"
|
||||||
#include "env-file.h"
|
#include "env-file.h"
|
||||||
#include "errno-util.h"
|
#include "errno-util.h"
|
||||||
#include "escape.h"
|
#include "escape.h"
|
||||||
@@ -84,6 +85,10 @@ int user_new(Manager *m, UserRecord *ur, User **ret) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
r = unit_name_build("systemd-pcrlogin", lu, ".service", &u->measure_unit);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
r = hashmap_put(m->users, UID_TO_PTR(ur->uid), u);
|
r = hashmap_put(m->users, UID_TO_PTR(ur->uid), u);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@@ -100,6 +105,10 @@ int user_new(Manager *m, UserRecord *ur, User **ret) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
r = hashmap_put(m->user_units, u->measure_unit, u);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
*ret = TAKE_PTR(u);
|
*ret = TAKE_PTR(u);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -118,15 +127,21 @@ User *user_free(User *u) {
|
|||||||
|
|
||||||
if (u->service_manager_unit) {
|
if (u->service_manager_unit) {
|
||||||
(void) hashmap_remove_value(u->manager->user_units, u->service_manager_unit, u);
|
(void) hashmap_remove_value(u->manager->user_units, u->service_manager_unit, u);
|
||||||
free(u->service_manager_job);
|
|
||||||
free(u->service_manager_unit);
|
free(u->service_manager_unit);
|
||||||
}
|
}
|
||||||
|
free(u->service_manager_job);
|
||||||
|
|
||||||
if (u->runtime_dir_unit) {
|
if (u->runtime_dir_unit) {
|
||||||
(void) hashmap_remove_value(u->manager->user_units, u->runtime_dir_unit, u);
|
(void) hashmap_remove_value(u->manager->user_units, u->runtime_dir_unit, u);
|
||||||
free(u->runtime_dir_job);
|
|
||||||
free(u->runtime_dir_unit);
|
free(u->runtime_dir_unit);
|
||||||
}
|
}
|
||||||
|
free(u->runtime_dir_job);
|
||||||
|
|
||||||
|
if (u->measure_unit) {
|
||||||
|
(void) hashmap_remove_value(u->manager->user_units, u->measure_unit, u);
|
||||||
|
free(u->measure_unit);
|
||||||
|
}
|
||||||
|
free(u->measure_job);
|
||||||
|
|
||||||
if (u->slice) {
|
if (u->slice) {
|
||||||
(void) hashmap_remove_value(u->manager->user_units, u->slice, u);
|
(void) hashmap_remove_value(u->manager->user_units, u->slice, u);
|
||||||
@@ -177,6 +192,7 @@ static int user_save_internal(User *u) {
|
|||||||
env_file_fputs_assignment(f, "RUNTIME=", u->runtime_path);
|
env_file_fputs_assignment(f, "RUNTIME=", u->runtime_path);
|
||||||
env_file_fputs_assignment(f, "RUNTIME_DIR_JOB=", u->runtime_dir_job);
|
env_file_fputs_assignment(f, "RUNTIME_DIR_JOB=", u->runtime_dir_job);
|
||||||
env_file_fputs_assignment(f, "SERVICE_JOB=", u->service_manager_job);
|
env_file_fputs_assignment(f, "SERVICE_JOB=", u->service_manager_job);
|
||||||
|
env_file_fputs_assignment(f, "MEASURE_JOB=", u->measure_job);
|
||||||
if (u->display)
|
if (u->display)
|
||||||
env_file_fputs_assignment(f, "DISPLAY=", u->display->id);
|
env_file_fputs_assignment(f, "DISPLAY=", u->display->id);
|
||||||
|
|
||||||
@@ -303,6 +319,7 @@ int user_load(User *u) {
|
|||||||
r = parse_env_file(NULL, u->state_file,
|
r = parse_env_file(NULL, u->state_file,
|
||||||
"RUNTIME_DIR_JOB", &u->runtime_dir_job,
|
"RUNTIME_DIR_JOB", &u->runtime_dir_job,
|
||||||
"SERVICE_JOB", &u->service_manager_job,
|
"SERVICE_JOB", &u->service_manager_job,
|
||||||
|
"MEASURE_JOB", &u->measure_job,
|
||||||
"STOPPING", &stopping,
|
"STOPPING", &stopping,
|
||||||
"REALTIME", &realtime,
|
"REALTIME", &realtime,
|
||||||
"MONOTONIC", &monotonic,
|
"MONOTONIC", &monotonic,
|
||||||
@@ -358,6 +375,39 @@ static int user_start_runtime_dir(User *u) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int user_start_measure(User *u) {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(u);
|
||||||
|
assert(!u->stopping);
|
||||||
|
assert(u->manager);
|
||||||
|
assert(u->measure_unit);
|
||||||
|
|
||||||
|
/* Measures the user's record into the 'login' TPM2 NvPCR on the first login of this boot. This is
|
||||||
|
* strictly best-effort: a measurement failure must never block the login (simply because NvPCRs might
|
||||||
|
* not be available). Unlike the runtime-dir and service-manager units, this one is started but never
|
||||||
|
* stopped (see user_stop_service()): it is a Type=oneshot/RemainAfterExit unit living in
|
||||||
|
* system.slice that stays active until reboot, so a later re-login's StartUnit is a no-op and every
|
||||||
|
* user is measured exactly once per boot.
|
||||||
|
*
|
||||||
|
* Only bother if we're actually running in measured-OS mode. The unit itself also carries
|
||||||
|
* ConditionSecurity=measured-os, but checking here avoids queuing a no-op job on every first login
|
||||||
|
* on systems that don't measure the OS */
|
||||||
|
if (efi_measured_os(LOG_DEBUG) <= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
u->measure_job = mfree(u->measure_job);
|
||||||
|
|
||||||
|
r = manager_start_unit(u->manager, u->measure_unit, &error, &u->measure_job);
|
||||||
|
if (r < 0)
|
||||||
|
return log_full_errno(sd_bus_error_has_name(&error, BUS_ERROR_UNIT_MASKED) ? LOG_DEBUG : LOG_WARNING,
|
||||||
|
r, "Failed to start user measurement service '%s', ignoring: %s",
|
||||||
|
u->measure_unit, bus_error_message(&error, r));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static bool user_wants_service_manager(const User *u) {
|
static bool user_wants_service_manager(const User *u) {
|
||||||
assert(u);
|
assert(u);
|
||||||
|
|
||||||
@@ -512,6 +562,14 @@ int user_start(User *u) {
|
|||||||
/* Set slice parameters */
|
/* Set slice parameters */
|
||||||
(void) user_update_slice(u);
|
(void) user_update_slice(u);
|
||||||
|
|
||||||
|
/* Measure the user record into the 'login' NvPCR. We do this *before* starting the runtime dir
|
||||||
|
* (and, further below, the service manager): manager_start_unit() is synchronous, so by the
|
||||||
|
* time those units are enqueued the measurement's start job already exists, and their
|
||||||
|
* After=systemd-pcrlogin@%i.service ordering then guarantees the measurement completes before
|
||||||
|
* the user's session becomes available. Best-effort and never stopped again, so each user is
|
||||||
|
* measured exactly once per boot. */
|
||||||
|
(void) user_start_measure(u);
|
||||||
|
|
||||||
(void) user_start_runtime_dir(u);
|
(void) user_start_runtime_dir(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,13 @@ typedef struct User {
|
|||||||
char *service_manager_unit;
|
char *service_manager_unit;
|
||||||
char *service_manager_job;
|
char *service_manager_job;
|
||||||
|
|
||||||
|
/* systemd-pcrlogin@UID.service — measures the user record into the 'login' NvPCR on first login.
|
||||||
|
* Unlike the units above, logind starts this but never stops it: it must run exactly once per boot,
|
||||||
|
* so it lives in system.slice, stays active until reboot, and is intentionally absent from
|
||||||
|
* user_stop_service(). */
|
||||||
|
char *measure_unit;
|
||||||
|
char *measure_job;
|
||||||
|
|
||||||
Session *display;
|
Session *display;
|
||||||
|
|
||||||
dual_timestamp timestamp; /* When this User object was 'started' the first time */
|
dual_timestamp timestamp; /* When this User object was 'started' the first time */
|
||||||
|
|||||||
@@ -10,7 +10,10 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=User Runtime Directory /run/user/%i
|
Description=User Runtime Directory /run/user/%i
|
||||||
Documentation=man:user@.service(5)
|
Documentation=man:user@.service(5)
|
||||||
After=systemd-logind.service dbus.service
|
# Order after the user record measurement (started by logind before this unit), so that the user's record is
|
||||||
|
# measured into the 'login' TPM2 NvPCR before the runtime dir — and thus the user's session — becomes
|
||||||
|
# available. This is ordering only (no Wants=/Requires=): a measurement failure must never block login.
|
||||||
|
After=systemd-logind.service dbus.service systemd-pcrlogin@%i.service
|
||||||
IgnoreOnIsolate=yes
|
IgnoreOnIsolate=yes
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
|||||||
@@ -11,7 +11,9 @@
|
|||||||
Description=User Manager for UID %i
|
Description=User Manager for UID %i
|
||||||
Documentation=man:user@.service(5)
|
Documentation=man:user@.service(5)
|
||||||
BindsTo=user-runtime-dir@%i.service
|
BindsTo=user-runtime-dir@%i.service
|
||||||
After=systemd-logind.service user-runtime-dir@%i.service dbus.service systemd-oomd.service
|
# Order after the user record measurement (see user-runtime-dir@.service), so the user's record is measured
|
||||||
|
# into the 'login' TPM2 NvPCR before the user manager starts. Ordering only, so login is never blocked on it.
|
||||||
|
After=systemd-logind.service user-runtime-dir@%i.service dbus.service systemd-oomd.service systemd-pcrlogin@%i.service
|
||||||
IgnoreOnIsolate=yes
|
IgnoreOnIsolate=yes
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
|||||||
Reference in New Issue
Block a user