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
#include <sys/mount.h>
#include "acl-util.h"
+#include "cgroup.h"
#include "creds-util.h"
#include "errno-util.h"
#include "exec-credential.h"
!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;
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;
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
now(CLOCK_REALTIME),
getuid(),
&IOVEC_MAKE(data, size),
- /* flags= */ 0, /* only allow user creds in user scope */
+ flags,
&plaintext);
break;
static int acquire_credentials(
const ExecContext *context,
+ const CGroupContext *cgroup_context,
const ExecParameters *params,
const char *unit,
const char *p,
int r;
assert(context);
+ assert(cgroup_context);
assert(params);
assert(unit);
assert(p);
struct load_cred_args args = {
.context = context,
+ .cgroup_context = cgroup_context,
.params = params,
.unit = unit,
.write_dfd = dfd,
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 */
(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
int exec_setup_credentials(
const ExecContext *context,
+ const CGroupContext *cgroup_context,
const ExecParameters *params,
const char *unit,
uid_t uid,
r = setup_credentials_internal(
context,
+ cgroup_context,
params,
unit,
p, /* final mount point */
r = setup_credentials_internal(
context,
+ cgroup_context,
params,
unit,
p, /* final mount point */