From: Daan De Meyer Date: Sun, 5 Apr 2026 17:12:35 +0000 (+0000) Subject: swtpm: gracefully fall back when --print-profiles output is not JSON X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4753c60053aea00d650e103a5eda893fd47c022b;p=thirdparty%2Fsystemd.git swtpm: gracefully fall back when --print-profiles output is not JSON 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. --- diff --git a/src/shared/swtpm-util.c b/src/shared/swtpm-util.c index 55e3f2f34c5..61413e7226a 100644 --- a/src/shared/swtpm-util.c +++ b/src/shared/swtpm-util.c @@ -18,20 +18,16 @@ #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",