]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
creds: allow varlink clients to choose --with-key= like on the command line
authorLennart Poettering <lennart@poettering.net>
Wed, 10 Sep 2025 20:45:57 +0000 (22:45 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 8 Oct 2025 07:18:28 +0000 (09:18 +0200)
src/creds/creds.c
src/shared/varlink-io.systemd.Credentials.c

index e2aaccfe79017094d851db7b94e7785702e1f7bc..7c8731fbf6a3b68e6f3d6b90b80ad6b5537faa0f 100644 (file)
@@ -1167,6 +1167,7 @@ typedef struct MethodEncryptParameters {
         uint64_t timestamp;
         uint64_t not_after;
         CredentialScope scope;
+        sd_id128_t with_key;
         uid_t uid;
 } MethodEncryptParameters;
 
@@ -1220,6 +1221,71 @@ static int settle_scope(
         return 0;
 }
 
+static bool normalize_separator(char c) {
+        if (IN_SET(c, '-', '+', '_'))
+                return '_';
+
+        return c;
+}
+
+static bool enum_name_equal(const char *x, const char *y) {
+        if (x == y)
+                return true;
+        if (!x || !y)
+                return false;
+
+        for (;; x++, y++) {
+                char a = normalize_separator(*x), b = normalize_separator(*y);
+                if (a != b)
+                        return false;
+                if (a == 0)
+                        return true;
+        }
+}
+
+static CredKeyType cred_key_type_from_string_harder(const char *s) {
+        if (!s)
+                return _CRED_KEY_TYPE_INVALID;
+
+        CredKeyType t = cred_key_type_from_string(s);
+        if (t >= 0)
+                return t;
+
+        /* Varlink doesn't like dashes and plusses in enum names. Try to match when considering them equal to underscores */
+        for (t = 0; t < _CRED_KEY_TYPE_MAX; t++)
+                if (enum_name_equal(cred_key_type_table[t], s))
+                        return t;
+
+        return _CRED_KEY_TYPE_INVALID;
+}
+
+static int dispatch_credential_key_type(
+                const char *name,
+                sd_json_variant *variant,
+                sd_json_dispatch_flags_t flags,
+                void *userdata) {
+
+        sd_id128_t *id = ASSERT_PTR(userdata);
+
+        if (sd_json_variant_is_null(variant)) {
+                *id = SD_ID128_NULL;
+                return 0;
+        }
+
+        const char *s = sd_json_variant_string(variant);
+        if (isempty(s)) {
+                *id = SD_ID128_NULL;
+                return 0;
+        }
+
+        CredKeyType t = cred_key_type_from_string_harder(s);
+        if (t < 0)
+                return json_log(variant, flags, t, "JSON field '%s' is not a valid key type.", strna(name));
+
+        *id = cred_key_id[t];
+        return 0;
+}
+
 static int vl_method_encrypt(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
 
         static const sd_json_dispatch_field dispatch_table[] = {
@@ -1229,6 +1295,7 @@ static int vl_method_encrypt(sd_varlink *link, sd_json_variant *parameters, sd_v
                 { "timestamp", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       offsetof(MethodEncryptParameters, timestamp), 0 },
                 { "notAfter",  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       offsetof(MethodEncryptParameters, not_after), 0 },
                 { "scope",     SD_JSON_VARIANT_STRING,        dispatch_credential_scope,     offsetof(MethodEncryptParameters, scope),     0 },
+                { "withKey",   SD_JSON_VARIANT_STRING,        dispatch_credential_key_type,  offsetof(MethodEncryptParameters, with_key),  SD_JSON_NULLABLE },
                 { "uid",       _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,      offsetof(MethodEncryptParameters, uid),       0 },
                 VARLINK_DISPATCH_POLKIT_FIELD,
                 {}
@@ -1289,7 +1356,7 @@ static int vl_method_encrypt(sd_varlink *link, sd_json_variant *parameters, sd_v
         }
 
         r = encrypt_credential_and_warn(
-                        p.scope == CREDENTIAL_USER ? _CRED_AUTO_SCOPED : _CRED_AUTO,
+                        sd_id128_is_null(p.with_key) ? (p.scope == CREDENTIAL_USER ? _CRED_AUTO_SCOPED : _CRED_AUTO) : p.with_key,
                         p.name,
                         p.timestamp,
                         p.not_after,
index ac250c8a796b85c3b3aa184ed1738e5d527c06e8..c774604d3f27740e92187724060ef46e9b36dad3 100644 (file)
@@ -10,6 +10,25 @@ static SD_VARLINK_DEFINE_ENUM_TYPE(
                 SD_VARLINK_FIELD_COMMENT("Generate a system and user bound credential"),
                 SD_VARLINK_DEFINE_ENUM_VALUE(user));
 
+static SD_VARLINK_DEFINE_ENUM_TYPE(
+                WithKey,
+                SD_VARLINK_FIELD_COMMENT("Automatically pick the key to bind the credential to"),
+                SD_VARLINK_DEFINE_ENUM_VALUE(auto),
+                SD_VARLINK_FIELD_COMMENT("Automatically pick the key to bind the credential to, but ensure it is accessible in the initrd, thus potentially leaving it unencrypted."),
+                SD_VARLINK_DEFINE_ENUM_VALUE(auto_initrd),
+                SD_VARLINK_FIELD_COMMENT("Bind to the host key only, i.e. not the TPM"),
+                SD_VARLINK_DEFINE_ENUM_VALUE(host),
+                SD_VARLINK_FIELD_COMMENT("Bind to the TPM only, not the host key"),
+                SD_VARLINK_DEFINE_ENUM_VALUE(tpm2),
+                SD_VARLINK_FIELD_COMMENT("Bind to the TPM only (using a public key identifying the UKI), not the host key"),
+                SD_VARLINK_DEFINE_ENUM_VALUE(tpm2_with_public_key),
+                SD_VARLINK_FIELD_COMMENT("Bind to both the TPM and the host key"),
+                SD_VARLINK_DEFINE_ENUM_VALUE(host_tpm2),
+                SD_VARLINK_FIELD_COMMENT("Bind to both the TPM (using a public key identifying the UKI) and the host key"),
+                SD_VARLINK_DEFINE_ENUM_VALUE(host_tpm2_with_public_key),
+                SD_VARLINK_FIELD_COMMENT("Do not bind to either host key nor the TPM, thus using null encryption (this provides no authenticity nor confidentiality guarantees)"),
+                SD_VARLINK_DEFINE_ENUM_VALUE(null));
+
 static SD_VARLINK_DEFINE_METHOD(
                 Encrypt,
                 SD_VARLINK_FIELD_COMMENT("The name for the encrypted credential, a string suitable for inclusion in a file name. If not specified no name is encoded in the credential. Typically, if this credential is stored on disk, this is how the file should be called, and permits authentication of the filename."),
@@ -24,6 +43,8 @@ static SD_VARLINK_DEFINE_METHOD(
                 SD_VARLINK_DEFINE_INPUT(notAfter, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("The intended scope for the credential. One of 'system' or 'user'. If not specified defaults to 'system', unless an uid is specified (see below), in which case it default to 'user'."),
                 SD_VARLINK_DEFINE_INPUT_BY_TYPE(scope, Scope, SD_VARLINK_NULLABLE),
+                SD_VARLINK_FIELD_COMMENT("Selects the type of key to encrypt this with"),
+                SD_VARLINK_DEFINE_INPUT_BY_TYPE(withKey, WithKey, SD_VARLINK_NULLABLE),
                 SD_VARLINK_FIELD_COMMENT("The numeric UNIX UID of the user the credential shall be scoped to. Only relevant if 'user' scope is selected (see above). If not specified and 'user' scope is selected defaults to the UID of the calling user, if that can be determined."),
                 SD_VARLINK_DEFINE_INPUT(uid, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
                 VARLINK_DEFINE_POLKIT_INPUT,
@@ -63,6 +84,8 @@ SD_VARLINK_DEFINE_INTERFACE(
                 SD_VARLINK_INTERFACE_COMMENT("APIs for encrypting and decrypting service credentials."),
                 SD_VARLINK_SYMBOL_COMMENT("The intended scope for the credential."),
                 &vl_type_Scope,
+                SD_VARLINK_SYMBOL_COMMENT("Selects the key(s) to encrypt the credentials with."),
+                &vl_type_WithKey,
                 SD_VARLINK_SYMBOL_COMMENT("Encrypts some plaintext data, returns an encrypted credential."),
                 &vl_method_Encrypt,
                 SD_VARLINK_SYMBOL_COMMENT("Decrypts an encrypted credential, returns plaintext data."),