various tweaks for systemd-creds & related (#38979)
This commit is contained in:
@@ -3915,6 +3915,13 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
|
||||
<varname>LoadCredential=</varname> and <varname>LoadCredentialEncrypted=</varname> take priority over
|
||||
credentials found by <varname>ImportCredential=</varname>.</para>
|
||||
|
||||
<para>Note that if decryption or authentication of a credential picked up as result of
|
||||
<varname>ImportCredential=</varname> fails it will be skipped gracefully (a warning is generated, but
|
||||
the credential will not be made available to the invoked service). This is different for those
|
||||
configured via
|
||||
<varname>SetCredentialEncrypted=</varname>/<varname>LoadCredentialEncrypted=</varname>, where failed
|
||||
decryption/authentication will result in service failure.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
+22
-13
@@ -463,7 +463,8 @@ static int maybe_decrypt_and_write_credential(
|
||||
struct load_cred_args *args,
|
||||
const char *id,
|
||||
const char *data,
|
||||
size_t size) {
|
||||
size_t size,
|
||||
bool graceful) {
|
||||
|
||||
_cleanup_(iovec_done_erase) struct iovec plaintext = {};
|
||||
size_t add;
|
||||
@@ -517,8 +518,14 @@ static int maybe_decrypt_and_write_credential(
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
if (r < 0)
|
||||
if (r < 0) {
|
||||
if (graceful) {
|
||||
log_warning_errno(r, "Unable to decrypt credential '%s', skipping.", id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
data = plaintext.iov_base;
|
||||
size = plaintext.iov_len;
|
||||
@@ -607,7 +614,7 @@ static int load_credential_glob(
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read credential '%s': %m", *p);
|
||||
|
||||
r = maybe_decrypt_and_write_credential(args, fn, data, size);
|
||||
r = maybe_decrypt_and_write_credential(args, fn, data, size, /* graceful= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -732,7 +739,7 @@ static int load_credential(
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read credential '%s': %m", path);
|
||||
|
||||
return maybe_decrypt_and_write_credential(args, id, data, size);
|
||||
return maybe_decrypt_and_write_credential(args, id, data, size, /* graceful= */ true);
|
||||
}
|
||||
|
||||
static int load_cred_recurse_dir_cb(
|
||||
@@ -869,10 +876,11 @@ static int acquire_credentials(
|
||||
|
||||
args.encrypted = false;
|
||||
|
||||
r = load_credential_glob(&args,
|
||||
ic,
|
||||
search_path,
|
||||
READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER);
|
||||
r = load_credential_glob(
|
||||
&args,
|
||||
ic,
|
||||
search_path,
|
||||
READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -884,10 +892,11 @@ static int acquire_credentials(
|
||||
|
||||
args.encrypted = true;
|
||||
|
||||
r = load_credential_glob(&args,
|
||||
ic,
|
||||
search_path,
|
||||
READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER|READ_FULL_FILE_UNBASE64);
|
||||
r = load_credential_glob(
|
||||
&args,
|
||||
ic,
|
||||
search_path,
|
||||
READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER|READ_FULL_FILE_UNBASE64);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -905,7 +914,7 @@ static int acquire_credentials(
|
||||
if (errno != ENOENT)
|
||||
return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sc->id);
|
||||
|
||||
r = maybe_decrypt_and_write_credential(&args, sc->id, sc->data, sc->size);
|
||||
r = maybe_decrypt_and_write_credential(&args, sc->id, sc->data, sc->size, /* graceful= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
+45
-33
@@ -1411,7 +1411,7 @@ static int vl_method_decrypt(sd_varlink *link, sd_json_variant *parameters, sd_v
|
||||
|
||||
if (r == -EBADMSG)
|
||||
return sd_varlink_error(link, "io.systemd.Credentials.BadFormat", NULL);
|
||||
if (r == -EREMOTE)
|
||||
if (r == -EDESTADDRREQ)
|
||||
return sd_varlink_error(link, "io.systemd.Credentials.NameMismatch", NULL);
|
||||
if (r == -ESTALE)
|
||||
return sd_varlink_error(link, "io.systemd.Credentials.TimeMismatch", NULL);
|
||||
@@ -1419,6 +1419,16 @@ static int vl_method_decrypt(sd_varlink *link, sd_json_variant *parameters, sd_v
|
||||
return sd_varlink_error(link, "io.systemd.Credentials.NoSuchUser", NULL);
|
||||
if (r == -EMEDIUMTYPE)
|
||||
return sd_varlink_error(link, "io.systemd.Credentials.BadScope", NULL);
|
||||
if (r == -EHOSTDOWN)
|
||||
return sd_varlink_error(link, "io.systemd.Credentials.CantFindPCRSignature", NULL);
|
||||
if (r == -EHWPOISON)
|
||||
return sd_varlink_error(link, "io.systemd.Credentials.NullKeyNotAllowed", NULL);
|
||||
if (r == -EREMOTE)
|
||||
return sd_varlink_error(link, "io.systemd.Credentials.KeyBelongsToOtherTPM", NULL);
|
||||
if (r == -ENOLCK)
|
||||
return sd_varlink_error(link, "io.systemd.Credentials.TPMInDictionaryLockout", NULL);
|
||||
if (IN_SET(r, -EREMCHG, -ENOANO, -EUCLEAN, -EPERM))
|
||||
return sd_varlink_error(link, "io.systemd.Credentials.UnexpectedPCRState", NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1433,6 +1443,38 @@ static int vl_method_decrypt(sd_varlink *link, sd_json_variant *parameters, sd_v
|
||||
return sd_varlink_reply(link, reply);
|
||||
}
|
||||
|
||||
static int vl_server(void) {
|
||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *polkit_registry = NULL;
|
||||
int r;
|
||||
|
||||
/* Invocation as Varlink service */
|
||||
|
||||
r = varlink_server_new(
|
||||
&varlink_server,
|
||||
SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA|SD_VARLINK_SERVER_INPUT_SENSITIVE,
|
||||
&polkit_registry);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate Varlink server: %m");
|
||||
|
||||
r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_Credentials);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add Varlink interface: %m");
|
||||
|
||||
r = sd_varlink_server_bind_method_many(
|
||||
varlink_server,
|
||||
"io.systemd.Credentials.Encrypt", vl_method_encrypt,
|
||||
"io.systemd.Credentials.Decrypt", vl_method_decrypt);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind Varlink methods: %m");
|
||||
|
||||
r = sd_varlink_server_loop_auto(varlink_server);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run Varlink event loop: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
int r;
|
||||
|
||||
@@ -1442,38 +1484,8 @@ static int run(int argc, char *argv[]) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (arg_varlink) {
|
||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *polkit_registry = NULL;
|
||||
|
||||
/* Invocation as Varlink service */
|
||||
|
||||
r = varlink_server_new(
|
||||
&varlink_server,
|
||||
SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA|SD_VARLINK_SERVER_INPUT_SENSITIVE,
|
||||
NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate Varlink server: %m");
|
||||
|
||||
r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_Credentials);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add Varlink interface: %m");
|
||||
|
||||
r = sd_varlink_server_bind_method_many(
|
||||
varlink_server,
|
||||
"io.systemd.Credentials.Encrypt", vl_method_encrypt,
|
||||
"io.systemd.Credentials.Decrypt", vl_method_decrypt);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to bind Varlink methods: %m");
|
||||
|
||||
sd_varlink_server_set_userdata(varlink_server, &polkit_registry);
|
||||
|
||||
r = sd_varlink_server_loop_auto(varlink_server);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to run Varlink event loop: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (arg_varlink)
|
||||
return vl_server();
|
||||
|
||||
return creds_main(argc, argv);
|
||||
}
|
||||
|
||||
+39
-14
@@ -1201,6 +1201,19 @@ int decrypt_credential_and_warn(
|
||||
assert(iovec_is_valid(input));
|
||||
assert(ret);
|
||||
|
||||
/* Relevant error codes:
|
||||
*
|
||||
* -EBADMSG → Corrupted file
|
||||
* -EOPNOTSUPP → Unsupported file type (could be: requires TPM but we have no TPM)
|
||||
* -EHOSTDOWN → Need PCR signature file, but couldn't find it
|
||||
* -EHWPOISON → Attempt to decode NULL key (and CREDENTIAL_ALLOW_NULL is off), but the system has a TPM and SecureBoot is on
|
||||
* -EMEDIUMTYPE → File has unexpected scope, i.e. user-scoped credential is attempted to be unlocked in system scope, or vice versa
|
||||
* -EDESTADDRREQ → Credential is incorrectly named (i.e. the authenticated name does not match the actual name)
|
||||
* -ESTALE → Credential's valdity has passed
|
||||
* -ESRCH → User specified for scope does not exist on this system
|
||||
*
|
||||
* (plus the various error codes tpm2_unseal() returns) */
|
||||
|
||||
h = (struct encrypted_credential_header*) input->iov_base;
|
||||
|
||||
/* The ID must fit in, for the current and all future formats */
|
||||
@@ -1218,8 +1231,10 @@ int decrypt_credential_and_warn(
|
||||
|
||||
if (with_tpm2_pk) {
|
||||
r = tpm2_load_pcr_signature(tpm2_signature_path, &signature_json);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN), "Couldn't find PCR signature file: %m");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load pcr signature: %m");
|
||||
return log_error_errno(r, "Failed to load PCR signature: %m");
|
||||
}
|
||||
|
||||
if (with_null && !FLAGS_SET(flags, CREDENTIAL_ALLOW_NULL)) {
|
||||
@@ -1234,7 +1249,7 @@ int decrypt_credential_and_warn(
|
||||
|
||||
if (efi_has_tpm2()) {
|
||||
if (is_efi_secure_boot())
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON),
|
||||
"Credential uses fixed key for fallback use when TPM2 is absent — but TPM2 is present, and SecureBoot is enabled, refusing.");
|
||||
|
||||
log_warning("Credential uses fixed key for use when TPM2 is absent, but TPM2 is present! Accepting anyway, since SecureBoot is disabled.");
|
||||
@@ -1354,7 +1369,7 @@ int decrypt_credential_and_warn(
|
||||
/* srk= */ NULL,
|
||||
&tpm2_key);
|
||||
if (r == -EREMOTE)
|
||||
return log_error_errno(r, "TPM key integrity check failed. Key enrolled in superblock most likely does not belong to this TPM.");
|
||||
return log_error_errno(r, "TPM key integrity check failed. Key most likely does not belong to this TPM.");
|
||||
if (ERRNO_IS_NEG_TPM2_UNSEAL_BAD_PCR(r))
|
||||
return log_error_errno(r, "TPM policy does not match current system state. Either system has been tempered with or policy out-of-date: %m");
|
||||
if (r < 0)
|
||||
@@ -1486,7 +1501,7 @@ int decrypt_credential_and_warn(
|
||||
if (r < 0 && r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SYSTEMD_CREDENTIAL_VALIDATE_NAME: %m");
|
||||
if (r != 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Embedded credential name '%s' does not match filename '%s', refusing.", embedded_name, validate_name);
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EDESTADDRREQ), "Embedded credential name '%s' does not match filename '%s', refusing.", embedded_name, validate_name);
|
||||
|
||||
log_debug("Embedded credential name '%s' does not match expected name '%s', but configured to use credential anyway.", embedded_name, validate_name);
|
||||
}
|
||||
@@ -1637,16 +1652,26 @@ int ipc_decrypt_credential(const char *validate_name, usec_t validate_timestamp,
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to call Decrypt() varlink call.");
|
||||
if (!isempty(error_id)) {
|
||||
if (streq(error_id, "io.systemd.Credentials.BadFormat"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Bad credential format.");
|
||||
if (streq(error_id, "io.systemd.Credentials.NameMismatch"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Name in credential doesn't match expectations.");
|
||||
if (streq(error_id, "io.systemd.Credentials.TimeMismatch"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ESTALE), "Outside of credential validity time window.");
|
||||
if (streq(error_id, "io.systemd.Credentials.NoSuchUser"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "No such user.");
|
||||
if (streq(error_id, "io.systemd.Credentials.BadScope"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Scope mismtach.");
|
||||
static struct {
|
||||
const char *id;
|
||||
int errnum;
|
||||
const char *msg;
|
||||
} table[] = {
|
||||
{ "io.systemd.Credentials.BadFormat", EBADMSG, "Bad credential format." },
|
||||
{ "io.systemd.Credentials.NameMismatch", EDESTADDRREQ, "Name in credential doesn't match expectations." },
|
||||
{ "io.systemd.Credentials.TimeMismatch", ESTALE, "Outside of credential validity time window." },
|
||||
{ "io.systemd.Credentials.NoSuchUser", ESRCH, "No such user." },
|
||||
{ "io.systemd.Credentials.BadScope", EMEDIUMTYPE, "Scope mismatch." },
|
||||
{ "io.systemd.Credentials.CantFindPCRSignature", EHOSTDOWN, "PCR signature required for decryption, but could not be found." },
|
||||
{ "io.systemd.Credentials.NullKeyNotAllowed", EHWPOISON, "The key was encrypted with a null key, but that's now allowed during decryption." },
|
||||
{ "io.systemd.Credentials.KeyBelongsToOtherTPM", EREMOTE, "The TPM integrity check for this key failed, key probably belongs to another TPM, or was corrupted." },
|
||||
{ "io.systemd.Credentials.TPMInDictionaryLockout", ENOLCK, "The TPM is in dictionary lockout mode, cannot operate." },
|
||||
{ "io.systemd.Credentials.UnexpectedPCRState" , EUCLEAN, "Unexpected TPM PCR state of the system." },
|
||||
};
|
||||
|
||||
FOREACH_ELEMENT(i, table)
|
||||
if (streq(i->id, error_id))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(i->errnum), "%s", i->msg);
|
||||
|
||||
return log_error_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to decrypt: %s", error_id);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,11 @@ static SD_VARLINK_DEFINE_ERROR(NameMismatch);
|
||||
static SD_VARLINK_DEFINE_ERROR(TimeMismatch);
|
||||
static SD_VARLINK_DEFINE_ERROR(NoSuchUser);
|
||||
static SD_VARLINK_DEFINE_ERROR(BadScope);
|
||||
static SD_VARLINK_DEFINE_ERROR(CantFindPCRSignature);
|
||||
static SD_VARLINK_DEFINE_ERROR(NullKeyNotAllowed);
|
||||
static SD_VARLINK_DEFINE_ERROR(KeyBelongsToOtherTPM);
|
||||
static SD_VARLINK_DEFINE_ERROR(TPMInDictionaryLockout);
|
||||
static SD_VARLINK_DEFINE_ERROR(UnexpectedPCRState);
|
||||
|
||||
SD_VARLINK_DEFINE_INTERFACE(
|
||||
io_systemd_Credentials,
|
||||
@@ -62,4 +67,14 @@ SD_VARLINK_DEFINE_INTERFACE(
|
||||
SD_VARLINK_SYMBOL_COMMENT("The specified user does not exist."),
|
||||
&vl_error_NoSuchUser,
|
||||
SD_VARLINK_SYMBOL_COMMENT("The credential does not match the selected scope."),
|
||||
&vl_error_BadScope);
|
||||
&vl_error_BadScope,
|
||||
SD_VARLINK_SYMBOL_COMMENT("PCR signature required for decryption, but not found."),
|
||||
&vl_error_CantFindPCRSignature,
|
||||
SD_VARLINK_SYMBOL_COMMENT("The key was encrypted with a null key, but that's now allowed during decryption."),
|
||||
&vl_error_NullKeyNotAllowed,
|
||||
SD_VARLINK_SYMBOL_COMMENT("The TPM integrity check for this key failed, key probably belongs to another TPM, or was corrupted."),
|
||||
&vl_error_KeyBelongsToOtherTPM,
|
||||
SD_VARLINK_SYMBOL_COMMENT("The TPM is in dictionary lockout mode, cannot operate."),
|
||||
&vl_error_TPMInDictionaryLockout,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Unexpected TPM PCR state of the system."),
|
||||
&vl_error_UnexpectedPCRState);
|
||||
|
||||
@@ -164,7 +164,7 @@ static void test_encrypt_decrypt_with(sd_id128_t mode, uid_t uid) {
|
||||
&encrypted,
|
||||
CREDENTIAL_ALLOW_NULL,
|
||||
&decrypted);
|
||||
ASSERT_ERROR(r, EREMOTE); /* name didn't match */
|
||||
ASSERT_ERROR(r, EDESTADDRREQ); /* name didn't match */
|
||||
|
||||
r = decrypt_credential_and_warn(
|
||||
"foo",
|
||||
|
||||
Reference in New Issue
Block a user