]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: when PrivateDevices= is enabled and we need to decrypt TPM2 credentials, go...
authorLennart Poettering <lennart@poettering.net>
Mon, 2 Jun 2025 11:31:29 +0000 (13:31 +0200)
committerMike Yuan <me@yhndnzj.com>
Tue, 24 Jun 2025 20:16:01 +0000 (22:16 +0200)
Also, if a device ACL list is defined, also go via IPC (instead of
trying to patch it, as before).

The outcome is that the tighter rules continue to apply when configured.

Fixes: #35959
NEWS
man/systemd.exec.xml
src/core/exec-credential.c
src/core/exec-credential.h
src/core/exec-invoke.c
src/core/unit.c

diff --git a/NEWS b/NEWS
index c317c20fbd860c3187958efd55c5a7e17875c5c6..acbaf968906a4da0e9b88b26d8b6bb094fc2749c 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -122,6 +122,19 @@ CHANGES WITH 258 in spe:
           GPG. The local keyring /etc/systemd/import-pubring.gpg is still parsed
           if present, to preserve backward compatibility.
 
+        * Normally, per-user encrypted credentials are decrypted via the the
+          systemd-creds.socket Varlink service, while the per-system ones are
+          directly encrypted within the execution context of the intended
+          service (which hence typically required access to /dev/tpmrm0). This
+          has been changed: units that enable either PrivateDevices= or use
+          DeviceAllow=/DevicePolicy= (and thus restrict access to device nodes)
+          will now also make use of the systemd-creds.socket Varlink
+          functionality, and will not attempt to decrypt the credentials
+          in-process (and attempt to try to talk to the TPM for
+          that). Previously, encrypted credentials for per-system services were
+          incompatible with PrivateDevices= and resulted in automatic extension
+          of the DeviceAllow= list. The latter behaviour has been removed.
+
         Announcements of Future Feature Removals:
 
         * Support for System V service scripts is deprecated and will be
index cd7b1056befa93efcf3193445a3d991b14186202..f9e790e492356bfead8e6d270536b4ab3459f22c 100644 (file)
@@ -3684,11 +3684,7 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
         authenticated credentials improves security as credentials are not stored in plaintext and only
         authenticated and decrypted into plaintext the moment a service requiring them is started. Moreover,
         credentials may be bound to the local hardware and installations, so that they cannot easily be
-        analyzed offline, or be generated externally. When <varname>DevicePolicy=</varname> is set to
-        <literal>closed</literal> or <literal>strict</literal>, or set to <literal>auto</literal> and
-        <varname>DeviceAllow=</varname> is set, or <varname>PrivateDevices=</varname> is set, then this
-        setting adds <filename>/dev/tpmrm0</filename> with <constant>rw</constant> mode to
-        <varname>DeviceAllow=</varname>. See
+        analyzed offline, or be generated externally. See
         <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         for the details about <varname>DevicePolicy=</varname> or <varname>DeviceAllow=</varname>.</para>
 
index 3077ddd92edcef19ccd305f00f69f0ff9bbaab6f..06a2794c82d4f00bf0eaec0a71e204fa3012bfe2 100644 (file)
@@ -3,6 +3,7 @@
 #include <sys/mount.h>
 
 #include "acl-util.h"
+#include "cgroup.h"
 #include "creds-util.h"
 #include "errno-util.h"
 #include "exec-credential.h"
@@ -237,22 +238,6 @@ bool exec_context_has_credentials(const ExecContext *c) {
                 !ordered_set_isempty(c->import_credentials);
 }
 
-bool exec_context_has_encrypted_credentials(const ExecContext *c) {
-        assert(c);
-
-        const ExecLoadCredential *load_cred;
-        HASHMAP_FOREACH(load_cred, c->load_credentials)
-                if (load_cred->encrypted)
-                        return true;
-
-        const ExecSetCredential *set_cred;
-        HASHMAP_FOREACH(set_cred, c->set_credentials)
-                if (set_cred->encrypted)
-                        return true;
-
-        return false;
-}
-
 bool mount_point_is_credentials(const char *runtime_prefix, const char *path) {
         const char *e;
 
@@ -445,8 +430,30 @@ static int credential_search_path(const ExecParameters *params, CredentialSearch
         return 0;
 }
 
+static bool device_nodes_restricted(
+                const ExecContext *c,
+                const CGroupContext *cgroup_context) {
+
+        assert(c);
+        assert(cgroup_context);
+
+        /* Returns true if we have any reason to believe we might not be able to access the TPM device
+         * directly, even if we run as root/PID 1. This could be because /dev/ is replaced by a private
+         * version, or because a device node access list is configured. */
+
+        if (c->private_devices)
+                return true;
+
+        if (cgroup_context->device_policy != CGROUP_DEVICE_POLICY_AUTO ||
+            cgroup_context->device_allow)
+                return true;
+
+        return false;
+}
+
 struct load_cred_args {
         const ExecContext *context;
+        const CGroupContext *cgroup_context;
         const ExecParameters *params;
         const char *unit;
         bool encrypted;
@@ -473,20 +480,30 @@ static int maybe_decrypt_and_write_credential(
         assert(data || size == 0);
 
         if (args->encrypted) {
+                CredentialFlags flags = 0; /* only allow user creds in user scope */
+
                 switch (args->params->runtime_scope) {
 
                 case RUNTIME_SCOPE_SYSTEM:
-                        /* In system mode talk directly to the TPM */
-                        r = decrypt_credential_and_warn(
-                                        id,
-                                        now(CLOCK_REALTIME),
-                                        /* tpm2_device= */ NULL,
-                                        /* tpm2_signature_path= */ NULL,
-                                        getuid(),
-                                        &IOVEC_MAKE(data, size),
-                                        CREDENTIAL_ANY_SCOPE,
-                                        &plaintext);
-                        break;
+                        /* In system mode talk directly to the TPM – unless we live in a device sandbox
+                         * which might block TPM device access. */
+
+                        flags |= CREDENTIAL_ANY_SCOPE;
+
+                        if (!device_nodes_restricted(args->context, args->cgroup_context)) {
+                                r = decrypt_credential_and_warn(
+                                                id,
+                                                now(CLOCK_REALTIME),
+                                                /* tpm2_device= */ NULL,
+                                                /* tpm2_signature_path= */ NULL,
+                                                getuid(),
+                                                &IOVEC_MAKE(data, size),
+                                                flags,
+                                                &plaintext);
+                                break;
+                        }
+
+                        _fallthrough_;
 
                 case RUNTIME_SCOPE_USER:
                         /* In per user mode we'll not have access to the machine secret, nor to the TPM (most
@@ -498,7 +515,7 @@ static int maybe_decrypt_and_write_credential(
                                         now(CLOCK_REALTIME),
                                         getuid(),
                                         &IOVEC_MAKE(data, size),
-                                        /* flags= */ 0, /* only allow user creds in user scope */
+                                        flags,
                                         &plaintext);
                         break;
 
@@ -770,6 +787,7 @@ static int load_cred_recurse_dir_cb(
 
 static int acquire_credentials(
                 const ExecContext *context,
+                const CGroupContext *cgroup_context,
                 const ExecParameters *params,
                 const char *unit,
                 const char *p,
@@ -781,6 +799,7 @@ static int acquire_credentials(
         int r;
 
         assert(context);
+        assert(cgroup_context);
         assert(params);
         assert(unit);
         assert(p);
@@ -795,6 +814,7 @@ static int acquire_credentials(
 
         struct load_cred_args args = {
                 .context = context,
+                .cgroup_context = cgroup_context,
                 .params = params,
                 .unit = unit,
                 .write_dfd = dfd,
@@ -921,6 +941,7 @@ static int acquire_credentials(
 
 static int setup_credentials_internal(
                 const ExecContext *context,
+                const CGroupContext *cgroup_context,
                 const ExecParameters *params,
                 const char *unit,
                 const char *final,        /* This is where the credential store shall eventually end up at */
@@ -1030,7 +1051,7 @@ static int setup_credentials_internal(
 
         (void) label_fix_full(AT_FDCWD, where, final, 0);
 
-        r = acquire_credentials(context, params, unit, where, uid, gid, workspace_mounted);
+        r = acquire_credentials(context, cgroup_context, params, unit, where, uid, gid, workspace_mounted);
         if (r < 0) {
                 /* If we're using final place as workspace, and failed to acquire credentials, we might
                  * have left half-written creds there. Let's get rid of the whole mount, so future
@@ -1074,6 +1095,7 @@ static int setup_credentials_internal(
 
 int exec_setup_credentials(
                 const ExecContext *context,
+                const CGroupContext *cgroup_context,
                 const ExecParameters *params,
                 const char *unit,
                 uid_t uid,
@@ -1140,6 +1162,7 @@ int exec_setup_credentials(
 
                 r = setup_credentials_internal(
                                 context,
+                                cgroup_context,
                                 params,
                                 unit,
                                 p,       /* final mount point */
@@ -1177,6 +1200,7 @@ int exec_setup_credentials(
 
                 r = setup_credentials_internal(
                                 context,
+                                cgroup_context,
                                 params,
                                 unit,
                                 p,           /* final mount point */
index c082d6d6f7e33023054c97db118a7c5f87488cdc..b712b71e6e6ff90817958c7e250e5a35d85d45bc 100644 (file)
@@ -44,7 +44,6 @@ int exec_context_put_import_credential(ExecContext *c, const char *glob, const c
 bool exec_params_need_credentials(const ExecParameters *p);
 
 bool exec_context_has_credentials(const ExecContext *c);
-bool exec_context_has_encrypted_credentials(const ExecContext *c);
 
 int exec_context_get_credential_directory(
                 const ExecContext *context,
@@ -56,6 +55,7 @@ int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_r
 
 int exec_setup_credentials(
                 const ExecContext *context,
+                const CGroupContext *cgroup_context,
                 const ExecParameters *params,
                 const char *unit,
                 uid_t uid,
index 9e7af81f1ef1259af29f99e1fe77a7d2e7247468..dc06754eca6f2edd569e7e1d8b5b026256fc64c1 100644 (file)
@@ -5272,7 +5272,7 @@ int exec_invoke(
                         return log_error_errno(r, "Failed to set up special execution directory in %s: %m", params->prefix[dt]);
         }
 
-        r = exec_setup_credentials(context, params, params->unit_id, uid, gid);
+        r = exec_setup_credentials(context, cgroup_context, params, params->unit_id, uid, gid);
         if (r < 0) {
                 *exit_status = EXIT_CREDENTIALS;
                 return log_error_errno(r, "Failed to set up credentials: %m");
index 1f46b96ad95e380d39e61f77c6f89e2da0026ea5..1d23d24cc878bac1ee06c7e70f118d8b28c82857 100644 (file)
@@ -4427,13 +4427,6 @@ int unit_patch_contexts(Unit *u) {
                                 if (r < 0)
                                         return r;
                         }
-
-                        /* If there are encrypted credentials we might need to access the TPM. */
-                        if (exec_context_has_encrypted_credentials(ec)) {
-                                r = cgroup_context_add_device_allow(cc, "char-tpm", CGROUP_DEVICE_READ|CGROUP_DEVICE_WRITE);
-                                if (r < 0)
-                                        return r;
-                        }
                 }
         }