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.
This commit is contained in:
committed by
Daan De Meyer
parent
ba937b45be
commit
4753c60053
+71
-47
@@ -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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
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",
|
||||
|
||||
Reference in New Issue
Block a user