mirror of
https://github.com/systemd/systemd.git
synced 2026-08-09 09:32:04 +00:00
credentials: add policy that can allow key=null creds from the ESP (#42555)
This PR only sets the default to "relaxed" - I can change the default to "tofu" if desired. But for that we will also need to update the NEWS file to ensure everyone is aware of this new default. --- This PR adds a new `systemd.credentials-boot=` kernel commandline that allows to control if credentials with a `null` key are accepted. The possible options are: * strict: always insist on tpm encryption * tofu: allow null encryption in firstboot mode and when no tpm is available * relaxed: allow null encryption when sb is off, or no tpm is available * off: allow null encryption always The default is `relaxed` which is exactly the behavior we had before. This replaces the initial idea of using plaintext credentials at firstboot (thanks to Lennart for this nicer and simpler design). --- With that we can drop `- firstboot: optionally accept credentials at firstboot without authentication` from TODO.md
This commit is contained in:
@@ -274,6 +274,33 @@ SetCredentialEncrypted=foobar: \
|
||||
…
|
||||
```
|
||||
|
||||
### Null-Key Encryption and the Boot Policy
|
||||
|
||||
In some situations a credential must be provisioned to a machine that does not
|
||||
yet (or does not at all) possess a key to encrypt against, for example before
|
||||
first boot completes, or on systems without a TPM2 device. For these cases
|
||||
`systemd-creds encrypt --with-key=null` produces a credential wrapped in the
|
||||
normal `systemd-creds` envelope, but encrypted with a fixed zero-length key.
|
||||
Such a credential travels through the same path as any other encrypted
|
||||
credential (e.g. it may be dropped into the ESP under `/loader/credentials/` or
|
||||
`*.efi.extra.d/`), but it offers neither confidentiality nor authenticity.
|
||||
Because of that, accepting it is risky on a system that could do better, and
|
||||
whether it is accepted at boot is controlled by the `systemd.credentials_boot_policy=`
|
||||
kernel command line option:
|
||||
|
||||
| Mode | A null-key credential is accepted when… |
|
||||
|-----------|---------------------------------------------------|
|
||||
| `strict` | never |
|
||||
| `tofu` | this is the first boot, or no TPM2 is available |
|
||||
| `relaxed` | SecureBoot is off, or no TPM2 is available |
|
||||
| `off` | always |
|
||||
|
||||
The default is `relaxed`. This policy only governs the
|
||||
default acceptance decision; credentials encrypted against a host key or TPM2
|
||||
device are always accepted. See
|
||||
[systemd(1)](https://www.freedesktop.org/software/systemd/man/latest/systemd.html)
|
||||
for details.
|
||||
|
||||
## Inheritance from Container Managers, Hypervisors, Kernel Command Line, or the UEFI Boot Environment
|
||||
|
||||
Sometimes it is useful to parameterize whole systems the same way as services,
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
<term><varname>systemd.set_credential=</varname></term>
|
||||
<term><varname>systemd.set_credential_binary=</varname></term>
|
||||
<term><varname>systemd.import_credentials=</varname></term>
|
||||
<term><varname>systemd.credentials_boot_policy=</varname></term>
|
||||
<term><varname>systemd.reload_limit_interval_sec=</varname></term>
|
||||
<term><varname>systemd.reload_limit_burst=</varname></term>
|
||||
<term><varname>systemd.minimum_uptime_sec=</varname></term>
|
||||
|
||||
@@ -965,6 +965,41 @@
|
||||
<xi:include href="version-info.xml" xpointer="v251"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.credentials_boot_policy=</varname></term>
|
||||
|
||||
<listitem><para>Controls under which conditions a credential encrypted with a <literal>null</literal>
|
||||
key (i.e. a credential wrapped in a <command>systemd-creds</command> envelope but offering neither
|
||||
confidentiality nor authenticity, see
|
||||
<citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry>)
|
||||
is accepted at boot. This provides a fallback for provisioning credentials before a machine has a key
|
||||
to encrypt them against, for example during first boot or on systems without a TPM2 device. Takes
|
||||
one of <option>strict</option>, <option>tofu</option>, <option>relaxed</option> or
|
||||
<option>off</option>:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para><option>strict</option> never accepts a <literal>null</literal> key, i.e. always
|
||||
insists on proper encryption.</para></listitem>
|
||||
|
||||
<listitem><para><option>tofu</option> (trust on first use) accepts a <literal>null</literal> key
|
||||
during first boot, or when no TPM2 device is available.</para></listitem>
|
||||
|
||||
<listitem><para><option>relaxed</option> accepts a <literal>null</literal> key when SecureBoot is
|
||||
disabled, or when no TPM2 device is available.</para></listitem>
|
||||
|
||||
<listitem><para><option>off</option> always accepts a <literal>null</literal> key.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>Defaults to <option>relaxed</option>. Note that this policy only applies to the default acceptance
|
||||
decision; it has no effect on credentials encrypted against a host key or TPM2 device, which are
|
||||
always accepted.</para>
|
||||
|
||||
<para>For further information see <ulink url="https://systemd.io/CREDENTIALS">System and Service
|
||||
Credentials</ulink> documentation.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v262"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>quiet</varname></term>
|
||||
|
||||
|
||||
@@ -38,3 +38,27 @@ bool in_initrd(void) {
|
||||
void in_initrd_force(bool value) {
|
||||
saved_in_initrd = value;
|
||||
}
|
||||
|
||||
int in_first_boot(void) {
|
||||
static int first_boot_env_parse_cached = -1;
|
||||
int r;
|
||||
|
||||
if (first_boot_env_parse_cached >= 0)
|
||||
return first_boot_env_parse_cached;
|
||||
|
||||
r = secure_getenv_bool("SYSTEMD_FIRST_BOOT");
|
||||
if (r >= 0)
|
||||
return (first_boot_env_parse_cached = r);
|
||||
if (r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SYSTEMD_FIRST_BOOT, ignoring: %m");
|
||||
|
||||
/* This is not cached and must *never* be cached, other parts of systemd write it to signal
|
||||
* an update to the first-boot state. */
|
||||
r = RET_NERRNO(access("/run/systemd/first-boot", F_OK));
|
||||
if (r >= 0)
|
||||
return true;
|
||||
if (r == -ENOENT)
|
||||
return false;
|
||||
|
||||
return log_debug_errno(r, "Failed to check /run/systemd/first-boot: %m");
|
||||
}
|
||||
|
||||
@@ -5,3 +5,5 @@
|
||||
|
||||
bool in_initrd(void);
|
||||
void in_initrd_force(bool value);
|
||||
|
||||
int in_first_boot(void);
|
||||
|
||||
@@ -2615,6 +2615,11 @@ static void log_execution_mode(bool *ret_first_boot) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Make the first-boot file visible now: the credential import
|
||||
* consults in_first_boot() for systemd.credentials_boot_policy= which runs
|
||||
* before the manager object is created. */
|
||||
(void) update_first_boot_file(first_boot);
|
||||
|
||||
assert_se(uname(&uts) >= 0);
|
||||
|
||||
if (strverscmp_improved(uts.release, KERNEL_BASELINE_VERSION) < 0)
|
||||
|
||||
@@ -4842,21 +4842,32 @@ fail:
|
||||
}
|
||||
|
||||
void manager_set_first_boot(Manager *m, bool b) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (!MANAGER_IS_SYSTEM(m))
|
||||
return;
|
||||
|
||||
if (m->first_boot != (int) b) {
|
||||
if (b)
|
||||
(void) touch("/run/systemd/first-boot");
|
||||
else
|
||||
(void) unlink("/run/systemd/first-boot");
|
||||
r = update_first_boot_file(b);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to update the first-boot file, ignoring: %m");
|
||||
}
|
||||
|
||||
m->first_boot = b;
|
||||
}
|
||||
|
||||
int update_first_boot_file(bool b) {
|
||||
if (b)
|
||||
return touch("/run/systemd/first-boot");
|
||||
|
||||
if (unlink("/run/systemd/first-boot") < 0 && errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void manager_disable_confirm_spawn(void) {
|
||||
(void) touch("/run/systemd/confirm_spawn_disabled");
|
||||
}
|
||||
|
||||
@@ -719,6 +719,8 @@ void manager_log_caller(Manager *manager, PidRef *caller, const char *method);
|
||||
|
||||
int manager_allocate_idle_pipe(Manager *m);
|
||||
|
||||
int update_first_boot_file(bool b);
|
||||
|
||||
void unit_defaults_init(UnitDefaults *defaults, RuntimeScope scope);
|
||||
void unit_defaults_done(UnitDefaults *defaults);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "format-table.h"
|
||||
#include "fs-util.h"
|
||||
#include "initrd-util.h"
|
||||
#include "io-util.h"
|
||||
#include "log.h"
|
||||
#include "main-func.h"
|
||||
@@ -88,8 +89,8 @@ static CreditEntropy may_credit(int seed_fd) {
|
||||
/* Don't credit the random seed if we are in first-boot mode, because we are supposed to start from
|
||||
* scratch. This is a safety precaution for cases where people ship "golden" images with empty
|
||||
* /etc but populated /var that contains a random seed. */
|
||||
r = RET_NERRNO(access("/run/systemd/first-boot", F_OK));
|
||||
if (r == -ENOENT)
|
||||
r = in_first_boot();
|
||||
if (r == 0)
|
||||
/* All is good, we are not in first-boot mode. */
|
||||
return CREDIT_ENTROPY_YES_PLEASE;
|
||||
if (r < 0) {
|
||||
|
||||
@@ -966,28 +966,6 @@ static int condition_test_needs_update(Condition *c, char **env) {
|
||||
return timespec_load_nsec(&usr.st_mtim) > timestamp;
|
||||
}
|
||||
|
||||
static bool in_first_boot(void) {
|
||||
static int first_boot = -1;
|
||||
int r;
|
||||
|
||||
if (first_boot >= 0)
|
||||
return first_boot;
|
||||
|
||||
const char *e = secure_getenv("SYSTEMD_FIRST_BOOT");
|
||||
if (e) {
|
||||
r = parse_boolean(e);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse $SYSTEMD_FIRST_BOOT, ignoring: %m");
|
||||
else
|
||||
return (first_boot = r);
|
||||
}
|
||||
|
||||
r = RET_NERRNO(access("/run/systemd/first-boot", F_OK));
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_debug_errno(r, "Failed to check if /run/systemd/first-boot exists, assuming no: %m");
|
||||
return r >= 0;
|
||||
}
|
||||
|
||||
static int condition_test_first_boot(Condition *c, char **env) {
|
||||
int r;
|
||||
|
||||
@@ -1002,7 +980,7 @@ static int condition_test_first_boot(Condition *c, char **env) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return in_first_boot() == r;
|
||||
return (in_first_boot() > 0) == r;
|
||||
}
|
||||
|
||||
static int condition_test_environment(Condition *c, char **env) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "find-esp.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "initrd-util.h"
|
||||
#include "io-util.h"
|
||||
#include "json-util.h"
|
||||
#include "log.h"
|
||||
@@ -31,10 +32,12 @@
|
||||
#include "mkdir.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "random-util.h"
|
||||
#include "recurse-dir.h"
|
||||
#include "sparse-endian.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "tpm2-pcr.h"
|
||||
@@ -366,6 +369,39 @@ int get_credential_user_password(const char *username, char **ret_password, bool
|
||||
return r;
|
||||
}
|
||||
|
||||
static const char* const credential_boot_policy_table[_CRED_BOOT_POLICY_MAX] = {
|
||||
[CRED_BOOT_STRICT] = "strict",
|
||||
[CRED_BOOT_TOFU] = "tofu",
|
||||
[CRED_BOOT_RELAXED] = "relaxed",
|
||||
[CRED_BOOT_OFF] = "off",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(credential_boot_policy, CredentialBootPolicy);
|
||||
|
||||
bool credential_boot_policy_accepts_null(CredentialBootPolicy policy, bool first_boot, bool have_tpm2, bool secure_boot) {
|
||||
|
||||
/* Decides whether a null-key encrypted credential (which offers neither confidentiality nor
|
||||
* authenticity) may be accepted, given the configured policy and the current system state. */
|
||||
|
||||
switch (policy) {
|
||||
|
||||
case CRED_BOOT_STRICT:
|
||||
return false;
|
||||
|
||||
case CRED_BOOT_TOFU:
|
||||
return first_boot || !have_tpm2;
|
||||
|
||||
case CRED_BOOT_RELAXED:
|
||||
return !secure_boot || !have_tpm2;
|
||||
|
||||
case CRED_BOOT_OFF:
|
||||
return true;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
|
||||
#define CREDENTIAL_HOST_SECRET_SIZE 4096
|
||||
@@ -1195,6 +1231,75 @@ int encrypt_credential_and_warn(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CredentialBootPolicy query_credential_boot_policy(void) {
|
||||
static CredentialBootPolicy cached = _CRED_BOOT_POLICY_INVALID;
|
||||
_cleanup_free_ char *value = NULL;
|
||||
int r;
|
||||
|
||||
if (cached >= 0)
|
||||
return cached;
|
||||
|
||||
/* default to RELAXED if invalid or unset */
|
||||
cached = CRED_BOOT_RELAXED;
|
||||
r = proc_cmdline_get_key("systemd.credentials_boot_policy", PROC_CMDLINE_STRIP_RD_PREFIX, &value);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to read systemd.credentials_boot_policy= from kernel command line, ignoring: %m");
|
||||
else if (r > 0) {
|
||||
CredentialBootPolicy p = credential_boot_policy_from_string(value);
|
||||
if (p < 0)
|
||||
log_warning("Invalid systemd.credentials_boot_policy= value '%s', ignoring.", value);
|
||||
else
|
||||
cached = p;
|
||||
}
|
||||
|
||||
return cached;
|
||||
}
|
||||
|
||||
static int check_null_key_policy(CredentialFlags flags) {
|
||||
if (FLAGS_SET(flags, CREDENTIAL_REFUSE_NULL))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON),
|
||||
"Credential uses null key, but that's not allowed, refusing.");
|
||||
|
||||
if (FLAGS_SET(flags, CREDENTIAL_ALLOW_NULL))
|
||||
return 0;
|
||||
|
||||
/* So this is a credential encrypted with a zero length key. We support this to cover for the
|
||||
* case where neither a host key not a TPM2 are available (specifically: initrd environments
|
||||
* where the host key is not yet accessible and no TPM2 chip exists at all), to minimize
|
||||
* different codeflow for TPM2 and non-TPM2 codepaths. Of course, credentials encoded this
|
||||
* way offer no confidentiality nor authenticity. Because of that it's important we refuse to
|
||||
* use them on systems that actually *do* have a TPM2 chip – if we are in SecureBoot
|
||||
* mode. Otherwise an attacker could hand us credentials like this and we'd use them thinking
|
||||
* they are trusted, even though they are not.
|
||||
*
|
||||
* Which conditions actually lead us to accept a null-key credential is configurable via
|
||||
* systemd.credentials_boot_policy=, which also covers the first boot case (before any key
|
||||
* exists yet); the decision itself is made in credential_boot_policy_accepts_null(). */
|
||||
|
||||
CredentialBootPolicy policy = query_credential_boot_policy();
|
||||
|
||||
bool have_tpm2 = efi_has_tpm2(), secure_boot = is_efi_secure_boot();
|
||||
/* in_first_boot() can return <0 on error: use the safe default in this case (not-first-boot). */
|
||||
bool first_boot = in_first_boot() > 0;
|
||||
|
||||
if (!credential_boot_policy_accepts_null(policy, first_boot, have_tpm2, secure_boot))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON),
|
||||
"Credential uses null key, but systemd.credentials_boot_policy=%s refuses it here (TPM2=%s, SecureBoot=%s, first boot=%s).",
|
||||
credential_boot_policy_to_string(policy),
|
||||
yes_no(have_tpm2), yes_no(secure_boot), yes_no(first_boot));
|
||||
|
||||
/* Accepting a null-key credential on a tpm2 host is undesired so keep it auditable */
|
||||
if (have_tpm2 && !first_boot)
|
||||
log_warning("Credential uses null key intended for use when TPM2 is absent, but TPM2 is present! "
|
||||
"Accepting anyway, under systemd.credentials_boot_policy=%s.",
|
||||
credential_boot_policy_to_string(policy));
|
||||
else
|
||||
log_debug("Credential uses null key, accepted under systemd.credentials_boot_policy=%s.",
|
||||
credential_boot_policy_to_string(policy));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int decrypt_credential_and_warn(
|
||||
const char *validate_name,
|
||||
usec_t validate_timestamp,
|
||||
@@ -1254,29 +1359,9 @@ int decrypt_credential_and_warn(
|
||||
}
|
||||
|
||||
if (sd_id128_equal(h->id, CRED_AES256_GCM_BY_NULL)) {
|
||||
if (FLAGS_SET(flags, CREDENTIAL_REFUSE_NULL))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON),
|
||||
"Credential uses null key, but that's not allowed, refusing.");
|
||||
|
||||
if (!FLAGS_SET(flags, CREDENTIAL_ALLOW_NULL)) {
|
||||
/* So this is a credential encrypted with a zero length key. We support this to cover for the
|
||||
* case where neither a host key not a TPM2 are available (specifically: initrd environments
|
||||
* where the host key is not yet accessible and no TPM2 chip exists at all), to minimize
|
||||
* different codeflow for TPM2 and non-TPM2 codepaths. Of course, credentials encoded this
|
||||
* way offer no confidentiality nor authenticity. Because of that it's important we refuse to
|
||||
* use them on systems that actually *do* have a TPM2 chip – if we are in SecureBoot
|
||||
* mode. Otherwise an attacker could hand us credentials like this and we'd use them thinking
|
||||
* they are trusted, even though they are not. */
|
||||
|
||||
if (efi_has_tpm2()) {
|
||||
if (is_efi_secure_boot())
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON),
|
||||
"Credential uses null key intended for fallback use when TPM2 is absent — but TPM2 is present, and SecureBoot is enabled, refusing.");
|
||||
|
||||
log_warning("Credential uses null key intended for use when TPM2 is absent, but TPM2 is present! Accepting anyway, since SecureBoot is disabled.");
|
||||
} else
|
||||
log_debug("Credential uses null key intended for use when TPM2 is absent, and TPM2 indeed is absent. Accepting.");
|
||||
}
|
||||
r = check_null_key_policy(flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (CRED_KEY_IS_SCOPED(h->id)) {
|
||||
|
||||
@@ -66,6 +66,19 @@ typedef enum CredentialFlags {
|
||||
CREDENTIAL_IPC_ALLOW_INTERACTIVE = 1 << 3,
|
||||
} CredentialFlags;
|
||||
|
||||
typedef enum CredentialBootPolicy {
|
||||
CRED_BOOT_STRICT, /* never accept a null key */
|
||||
CRED_BOOT_TOFU, /* accept a null key during first boot or when no TPM2 is available */
|
||||
CRED_BOOT_RELAXED, /* accept a null key when SecureBoot is off or when no TPM2 is available */
|
||||
CRED_BOOT_OFF, /* always accept a null key */
|
||||
_CRED_BOOT_POLICY_MAX,
|
||||
_CRED_BOOT_POLICY_INVALID = -EINVAL,
|
||||
} CredentialBootPolicy;
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP(credential_boot_policy, CredentialBootPolicy);
|
||||
|
||||
bool credential_boot_policy_accepts_null(CredentialBootPolicy policy, bool first_boot, bool have_tpm2, bool secure_boot);
|
||||
|
||||
/* The four modes we support: keyed only by on-disk key, only by TPM2 HMAC key, and by the combination of
|
||||
* both, as well as one with a fixed zero length key if TPM2 is missing (the latter of course provides no
|
||||
* authenticity or confidentiality, but is still useful for integrity protection, and makes things simpler
|
||||
|
||||
@@ -228,6 +228,35 @@ TEST(credential_encrypt_decrypt) {
|
||||
ASSERT_OK_ERRNO(setenv("SYSTEMD_CREDENTIAL_SECRET", ec, true));
|
||||
}
|
||||
|
||||
TEST(credential_boot_policy) {
|
||||
|
||||
/* String table round-trip */
|
||||
for (CredentialBootPolicy p = 0; p < _CRED_BOOT_POLICY_MAX; p++) {
|
||||
const char *s = credential_boot_policy_to_string(p);
|
||||
ASSERT_NOT_NULL(s);
|
||||
ASSERT_EQ(credential_boot_policy_from_string(s), p);
|
||||
}
|
||||
ASSERT_STREQ(credential_boot_policy_to_string(CRED_BOOT_TOFU), "tofu");
|
||||
ASSERT_TRUE(credential_boot_policy_from_string("bogus") < 0);
|
||||
|
||||
/* Exhaustively check the accept decision against every (first_boot, have_tpm2, secure_boot) state */
|
||||
for (int first_boot = 0; first_boot < 2; first_boot++)
|
||||
for (int have_tpm2 = 0; have_tpm2 < 2; have_tpm2++)
|
||||
for (int secure_boot = 0; secure_boot < 2; secure_boot++) {
|
||||
/* strict never accepts a null key, off always does */
|
||||
ASSERT_FALSE(credential_boot_policy_accepts_null(CRED_BOOT_STRICT, first_boot, have_tpm2, secure_boot));
|
||||
ASSERT_TRUE(credential_boot_policy_accepts_null(CRED_BOOT_OFF, first_boot, have_tpm2, secure_boot));
|
||||
|
||||
/* tofu accepts when first boot, or no TPM2 */
|
||||
ASSERT_EQ(credential_boot_policy_accepts_null(CRED_BOOT_TOFU, first_boot, have_tpm2, secure_boot),
|
||||
first_boot || !have_tpm2);
|
||||
|
||||
/* relaxed accepts when SecureBoot off, or no TPM2 */
|
||||
ASSERT_EQ(credential_boot_policy_accepts_null(CRED_BOOT_RELAXED, first_boot, have_tpm2, secure_boot),
|
||||
!secure_boot || !have_tpm2);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(mime_type_matches) {
|
||||
|
||||
static const sd_id128_t tags[] = {
|
||||
|
||||
@@ -168,6 +168,48 @@ systemd-creds decrypt /tmp/cred.enc /tmp/cred.dec
|
||||
diff /tmp/cred.orig /tmp/cred.dec
|
||||
rm -f /tmp/cred.{enc,dec}
|
||||
|
||||
# Null-key credentials and the systemd.credentials_boot_policy= setting.
|
||||
#
|
||||
# A null-key credential ("--with-key=null") is wrapped in the normal systemd-creds
|
||||
# envelope but offers neither confidentiality nor authenticity, so whether it is
|
||||
# accepted at decrypt time is governed by systemd.credentials_boot_policy= and the system
|
||||
# state. We only assert the branches that are deterministic regardless of the test
|
||||
# bed's TPM2/SecureBoot state (which is fixed and has no test override here); the
|
||||
# full accepts_null() truth table for all states is covered by test-creds.c.
|
||||
systemd-creds --name=nullcred --with-key=null encrypt /tmp/cred.orig /tmp/cred.enc
|
||||
|
||||
# --allow-null bypasses the policy, even under the strictest setting.
|
||||
SYSTEMD_PROC_CMDLINE="systemd.credentials_boot_policy=strict" \
|
||||
systemd-creds --name=nullcred --allow-null decrypt /tmp/cred.enc /tmp/cred.dec
|
||||
diff /tmp/cred.orig /tmp/cred.dec
|
||||
rm -f /tmp/cred.dec
|
||||
|
||||
# --refuse-null always refuses, even under the most permissive setting.
|
||||
(! SYSTEMD_PROC_CMDLINE="systemd.credentials_boot_policy=off" \
|
||||
systemd-creds --name=nullcred --refuse-null decrypt /tmp/cred.enc /tmp/cred.dec)
|
||||
|
||||
# strict never accepts a null key, regardless of system state.
|
||||
(! SYSTEMD_PROC_CMDLINE="systemd.credentials_boot_policy=strict" \
|
||||
systemd-creds --name=nullcred decrypt /tmp/cred.enc /tmp/cred.dec)
|
||||
|
||||
# off always accepts a null key, regardless of system state.
|
||||
SYSTEMD_PROC_CMDLINE="systemd.credentials_boot_policy=off" \
|
||||
systemd-creds --name=nullcred decrypt /tmp/cred.enc /tmp/cred.dec
|
||||
diff /tmp/cred.orig /tmp/cred.dec
|
||||
rm -f /tmp/cred.dec
|
||||
|
||||
# An invalid policy value falls back to the default (relaxed), which accepts during first boot.
|
||||
SYSTEMD_PROC_CMDLINE="systemd.credentials_boot_policy=bogus" SYSTEMD_FIRST_BOOT=1 \
|
||||
systemd-creds --name=nullcred decrypt /tmp/cred.enc /tmp/cred.dec
|
||||
diff /tmp/cred.orig /tmp/cred.dec
|
||||
rm -f /tmp/cred.dec
|
||||
|
||||
# tofu accepts during first boot, independent of TPM2/SecureBoot.
|
||||
SYSTEMD_PROC_CMDLINE="systemd.credentials_boot_policy=tofu" SYSTEMD_FIRST_BOOT=1 \
|
||||
systemd-creds --name=nullcred decrypt /tmp/cred.enc /tmp/cred.dec
|
||||
diff /tmp/cred.orig /tmp/cred.dec
|
||||
rm -f /tmp/cred.{enc,dec}
|
||||
|
||||
(! unshare -m bash -exc "mount -t tmpfs tmpfs /run/credentials && systemd-creds list")
|
||||
(! unshare -m bash -exc "mount -t tmpfs tmpfs /run/credentials && systemd-creds --system list")
|
||||
(! CREDENTIALS_DIRECTORY="" systemd-creds list)
|
||||
|
||||
Reference in New Issue
Block a user