]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
swtpm: gracefully fall back when --print-profiles output is not JSON
authorDaan De Meyer <daan@amutable.com>
Sun, 5 Apr 2026 17:12:35 +0000 (17:12 +0000)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Sun, 5 Apr 2026 17:47:26 +0000 (19:47 +0200)
Older swtpm versions print --help output instead of JSON when
swtpm_setup --print-profiles is invoked. Previously, the JSON parse
failure was treated as fatal, preventing swtpm manufacture entirely on
these older versions.

Extract profile detection into a separate swtpm_find_best_profile()
helper and treat JSON parse failure as a graceful fallback: log a
notice and continue without a profile, same as when no builtin profiles
are found.

src/shared/swtpm-util.c

index 55e3f2f34c52a1a59f5b16ba0f954f6975f1ec86..61413e7226aadb0700fbfc6cdf5ebe6e698be3ad 100644 (file)
 #include "strv.h"
 #include "swtpm-util.h"
 
-int manufacture_swtpm(const char *state_dir, const char *secret) {
+static int swtpm_find_best_profile(const char *swtpm_setup, char **ret) {
         int r;
 
-        assert(state_dir);
-
-        _cleanup_free_ char *swtpm_setup = NULL;
-        r = find_executable("swtpm_setup", &swtpm_setup);
-        if (r < 0)
-                return log_error_errno(r, "Failed to find 'swtpm_setup' binary: %m");
+        assert(swtpm_setup);
+        assert(ret);
 
         _cleanup_strv_free_ char **args = strv_new(
                         swtpm_setup,
-                         "--tpm2",
-                         "--print-profiles");
+                        "--tpm2",
+                        "--print-profiles");
         if (!args)
                 return log_oom();
 
@@ -83,44 +79,72 @@ int manufacture_swtpm(const char *state_dir, const char *secret) {
                 return log_error_errno(r, "Failed to read memory fd: %m");
 
         _cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
+        r = sd_json_parse(text, SD_JSON_PARSE_MUST_BE_OBJECT, &j, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
+        if (r < 0) {
+                log_notice("Failed to parse swtpm's --print-profiles output as JSON, assuming the implementation is too old to know the concept of profiles.");
+                *ret = NULL;
+                return 0;
+        }
+
+        sd_json_variant *v = sd_json_variant_by_key(j, "builtin");
+        if (!v) {
+                *ret = NULL;
+                return 0;
+        }
+
+        if (!sd_json_variant_is_array(v))
+                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "'builtin' field is not an array.");
+
         const char *best_profile = NULL;
-        if (isempty(text))
-                log_notice("No list of supported profiles could be acquired from swtpm, assuming the implementation is too old to know the concept of profiles.");
-        else {
-                r = sd_json_parse(text, SD_JSON_PARSE_MUST_BE_OBJECT, &j, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to parse swtpm's --print-profiles output: %m");
-
-                sd_json_variant *v = sd_json_variant_by_key(j, "builtin");
-                if (v) {
-                        if (!sd_json_variant_is_array(v))
-                                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "'builtin' field is not an array: %m");
-
-                        sd_json_variant *i;
-                        JSON_VARIANT_ARRAY_FOREACH(i, v) {
-                                if (!sd_json_variant_is_object(i))
-                                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Profile object is not a JSON object.");
-
-                                sd_json_variant *n = sd_json_variant_by_key(i, "Name");
-                                if (!n) {
-                                        log_debug("Object in profiles array does not have a 'Name', skipping.");
-                                        continue;
-                                }
-
-                                if (!sd_json_variant_is_string(n))
-                                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Profile's 'Name' field is not a string.");
-
-                                const char *s = sd_json_variant_string(n);
-
-                                /* Pick the best of the default-v1, default-v2, … profiles */
-                                if (!startswith(s, "default-v"))
-                                        continue;
-                                if (!best_profile || strverscmp_improved(s, best_profile) > 0)
-                                        best_profile = s;
-                        }
+        sd_json_variant *i;
+        JSON_VARIANT_ARRAY_FOREACH(i, v) {
+                if (!sd_json_variant_is_object(i))
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Profile object is not a JSON object.");
+
+                sd_json_variant *n = sd_json_variant_by_key(i, "Name");
+                if (!n) {
+                        log_debug("Object in profiles array does not have a 'Name', skipping.");
+                        continue;
                 }
+
+                if (!sd_json_variant_is_string(n))
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Profile's 'Name' field is not a string.");
+
+                const char *s = sd_json_variant_string(n);
+
+                /* Pick the best of the default-v1, default-v2, … profiles */
+                if (!startswith(s, "default-v"))
+                        continue;
+                if (!best_profile || strverscmp_improved(s, best_profile) > 0)
+                        best_profile = s;
+        }
+
+        _cleanup_free_ char *copy = NULL;
+        if (best_profile) {
+                copy = strdup(best_profile);
+                if (!copy)
+                        return log_oom();
         }
 
+        *ret = TAKE_PTR(copy);
+        return 0;
+}
+
+int manufacture_swtpm(const char *state_dir, const char *secret) {
+        int r;
+
+        assert(state_dir);
+
+        _cleanup_free_ char *swtpm_setup = NULL;
+        r = find_executable("swtpm_setup", &swtpm_setup);
+        if (r < 0)
+                return log_error_errno(r, "Failed to find 'swtpm_setup' binary: %m");
+
+        _cleanup_free_ char *best_profile = NULL;
+        r = swtpm_find_best_profile(swtpm_setup, &best_profile);
+        if (r < 0)
+                return r;
+
         /* Create custom swtpm config files so that swtpm_localca uses our state directory instead of
          * the system-wide /var/lib/swtpm-localca/ which may not be writable. */
         _cleanup_free_ char *localca_conf = path_join(state_dir, "swtpm-localca.conf");
@@ -172,8 +196,8 @@ int manufacture_swtpm(const char *state_dir, const char *secret) {
         if (r < 0)
                 return log_error_errno(r, "Failed to write swtpm_setup.conf: %m");
 
-        strv_free(args);
-        args = strv_new(swtpm_setup,
+        _cleanup_strv_free_ char **args = strv_new(
+                        swtpm_setup,
                         "--tpm-state", state_dir,
                         "--tpm2",
                         "--pcr-banks", "sha256",