mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
core: when PrivateDevices= is enabled and we need to decrypt TPM2 credentials, go via IPC
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
This commit is contained in:
committed by
Mike Yuan
parent
aecb6eaed7
commit
2be3a06bb2
13
NEWS
13
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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user