/
githubmirror
/
swtpm
Обзор
Документация
Войти
/
githubmirror
/
swtpm
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/swtpm_setup/profile.c
392 строки
11 KB
Stefan Berger
swtpm_setup: Use DISTRO_PROFILES_DIR when listing profiles (fix path issue)
26 мар 2025, 15:41
26 мар 2025, 15:41
2c4cd12
Код
Авторство
О чём код?
/* SPDX-License-Identifier: BSD-3-Clause */ /* * profile.c: TPM 2 profile handling * * Author: Stefan Berger, stefanb@linux.ibm.com * * Copyright (c) IBM Corporation, 2022 */ #include "config.h" #include <stdio.h> #include <string.h> #include <glib.h> #include <glib/gstdio.h> #include <json-glib/json-glib.h> #include "profile.h" #include "swtpm_conf.h" #include "swtpm_utils.h" #include "swtpm_setup_utils.h" #include "compiler_dependencies.h" #define DISTRO_PROFILES_DIR DATAROOTDIR "/swtpm/profiles" /* Return the names of the supported profiles */ int get_profile_names(const gchar *swtpm_capabilities_json, gchar ***profile_names) { g_autoptr(GError) error = NULL; JsonParser *jp = NULL; JsonReader *jr = NULL; JsonNode *root; gint i, num; int ret = 1; jp = json_parser_new(); if (!json_parser_load_from_data(jp, swtpm_capabilities_json, -1, &error)) { logerr(gl_LOGFILE, "Could not parse capabilities JSON '%s': %s\n", swtpm_capabilities_json, error->message); goto error_unref_jp; } root = json_parser_get_root(jp); jr = json_reader_new(root); if (!json_reader_read_member(jr, "profiles")) { logerr(gl_LOGFILE, "Missing 'profiles' field: %s\n", swtpm_capabilities_json); goto error_unref_jr; } if (!json_reader_read_member(jr, "names")) { logerr(gl_LOGFILE, "Missing 'names' field under 'profiles': %s\n", swtpm_capabilities_json); goto error_unref_jr; } num = json_reader_count_elements(jr); if (num < 0) { logerr(gl_LOGFILE, "Number of profile names is bad (%d)\n", num); goto error_unref_jr; } *profile_names = g_malloc0((num + 1) * sizeof(char *)); for (i = 0; i < num; i++) { if (!json_reader_read_element(jr, i)) { logerr(gl_LOGFILE, "Could not parse JSON list: %s\n", error->message); goto error_str_array_free; } (*profile_names)[i] = g_strdup(json_reader_get_string_value(jr)); json_reader_end_element(jr); } ret = 0; error_unref_jr: g_object_unref(jr); error_unref_jp: g_object_unref(jp); return ret; error_str_array_free: g_strfreev(*profile_names); goto error_unref_jr; } int check_json_profile(const gchar *swtpm_capabilities_json, const char *json_profile) { gchar **profile_names = NULL; g_autofree gchar *name = NULL; int idx; int ret; ret = json_get_map_value(json_profile, "Name", &name); /* { "Name": null } does not lead to parser failure but return name = NULL */ if (ret || name == NULL) { ret = 1; logerr(gl_LOGFILE, "Failed to get 'Name' from profile '%s'.\n", json_profile); return ret; } if (strlen(name) > 32) { logerr(gl_LOGFILE, "Profile name must not exceed 32 characters.\n"); return 1; } /* 'custom:' prefix is accepted */ if (!strncmp(name, "custom:", 7)) return 0; ret = get_profile_names(swtpm_capabilities_json, &profile_names); if (ret) goto error; idx = strv_strcmp(profile_names, name); if (idx < 0) { logerr(gl_LOGFILE, "swtpm does not support a profile with name '%s'\n", name); ret = 1; } error: g_strfreev(profile_names); return ret; } /* Create a path to the profile and check whether the file is accessible */ static int profile_path_from_dir(const gchar *dir, const gchar *profile_name, gchar **json_profile_file) { *json_profile_file = g_strdup_printf("%s/%s.json", dir, profile_name); if (g_access(*json_profile_file, R_OK) != 0) { SWTPM_G_FREE(*json_profile_file); return -1; } return 0; } static int profile_path_local(gchar *const *config_file_lines, const gchar *profile_name, gchar **json_profile_file) { g_autofree gchar *dir = NULL; dir = get_config_value(config_file_lines, "local_profiles_dir"); if (dir == NULL || strlen(dir) == 0 ) return -1; return profile_path_from_dir(dir, profile_name, json_profile_file); } static int profile_path_distro(gchar *const *config_file_lines SWTPM_ATTR_UNUSED, const gchar *profile_name, gchar **json_profile_file) { return profile_path_from_dir(DISTRO_PROFILES_DIR, profile_name, json_profile_file); } static int profile_build_json(gchar *const *config_file_lines SWTPM_ATTR_UNUSED, const gchar *profile_name, gchar **json_profile) { *json_profile = g_strdup_printf("{\"Name\":\"%s\"}", profile_name); return 0; } /* * Check whether the profile name is valid; especially avoid names with '/' * that would enable '../../etc/foo'. */ int profile_name_check(const gchar *profile_name) { GMatchInfo *match_info; GRegex *regex; int ret = 0; regex = g_regex_new("^[A-Za-z0-9.\\-:]+$", 0 /* G_REGEX_DEFAULT */, 0 /* G_REGEX_MATCH_DEFAULT */, NULL); g_regex_match(regex, profile_name, 0, &match_info); if (!g_match_info_matches(match_info)) { logerr(gl_LOGFILE, "Profile name '%s' contains unacceptable characters.\n", profile_name); ret = -1; } g_match_info_free(match_info); g_regex_unref(regex); return ret; } /* * Try to find the profile with the given name as a file (with added .json * suffix) in the local or distro files directories. If not found in either, * create the JSON for selecting a built-in profile. * * @config_file_lines: lines of the configuration file * @json_profile_name: the name of the profile to search for * @json_profile_file: pointer to set an available profile file's full path * @json_profile: pointer to set to the JSON string for built-in profile */ int profile_get_by_name(gchar *const *config_file_lines, const gchar *json_profile_name, gchar **json_profile_file, gchar **json_profile) { typedef int (*getter_t)(gchar *const *config_file_lines, const gchar *name, gchar **result); const struct { const char *prefix; gboolean filename; /* true: returns a filename */ getter_t getter; } prefixes[] = { { "local:", TRUE, profile_path_local}, { "distro:", TRUE, profile_path_distro}, { "builtin:", FALSE, profile_build_json}, { NULL, }, }; size_t i, len; for (i = 0; prefixes[i].prefix; i++) { len = strlen(prefixes[i].prefix); if (!strncmp(prefixes[i].prefix, json_profile_name, len)) return prefixes[i].getter(config_file_lines, &json_profile_name[len], prefixes[i].filename ? json_profile_file : json_profile); } /* no prefixes matched */ for (i = 0; prefixes[i].prefix; i++) { if (prefixes[i].getter(config_file_lines, json_profile_name, prefixes[i].filename ? json_profile_file : json_profile) == 0) return 0; } return -1; } static int profile_replace_name(JsonObject *jo, const char *name) { JsonNode *node; node = json_object_get_member(jo, "Name"); if (!node || json_node_get_node_type(node) != JSON_NODE_VALUE) return -1; json_object_set_string_member(jo, "Name", name); return 0; } static JsonArray *profile_gather_dir(const char *dir) { g_autofree gchar *fullpath = NULL; g_autoptr(JsonParser) jp = NULL; const gchar *filename; JsonNode *root; JsonObject *jo; JsonArray *ja; GDir *gdir; gdir = g_dir_open(dir, 0, NULL); if (!gdir) return NULL; jp = json_parser_new(); ja = json_array_new(); while ((filename = g_dir_read_name(gdir))) { size_t len = strlen(filename); if (len <= 5 || !g_str_has_suffix(filename, ".json")) continue; fullpath = g_strdup_printf("%s/%s", dir, filename); if (json_parser_load_from_file(jp, fullpath, NULL)) { root = json_parser_get_root(jp); if (json_node_get_node_type(root) == JSON_NODE_OBJECT) { /* remove suffix .json */ g_autofree gchar *name = g_strndup(filename, len - 5); jo = json_node_dup_object(root); if (profile_replace_name(jo, name) == 0) json_array_add_object_element(ja, jo); else json_object_unref(jo); } } SWTPM_G_FREE(fullpath); } g_dir_close(gdir); return ja; } static JsonArray *profile_gather_local(gchar *const *config_file_lines) { g_autofree gchar *dir = NULL; dir = get_config_value(config_file_lines, "local_profiles_dir"); if (dir == NULL || strlen(dir) == 0 ) return NULL; return profile_gather_dir(dir); } static JsonArray *profile_gather_builtin(const gchar **swtpm_prg_l) { g_autofree gchar *standard_output = NULL; g_autofree const gchar **cmd = NULL; g_autoptr(JsonParser) jp = NULL; JsonNode *root, *arr; JsonArray *ja = NULL; JsonObject *jo; cmd = concat_arrays(swtpm_prg_l, (const gchar*[]) {"--tpm2", "--print-profiles", NULL}, FALSE); if (!spawn_sync(NULL, cmd, NULL, G_SPAWN_STDERR_TO_DEV_NULL, NULL, NULL, &standard_output, NULL, NULL, NULL)) return NULL; jp = json_parser_new(); if (json_parser_load_from_data(jp, standard_output, -1, NULL)) { root = json_parser_get_root(jp); if (json_node_get_node_type(root) == JSON_NODE_OBJECT) { jo = json_node_get_object(root); arr = json_object_get_member(jo, "AvailableProfiles"); if (arr && json_node_get_node_type(arr) == JSON_NODE_ARRAY) ja = json_node_dup_array(arr); } } return ja; } /* * Gather all local, distro, and built-in profiles in a JSON map and print * to stdout. * * @swtpm_prg_l: Array of strings to invoke swtpm (e.g., 'swtpm socket') * @config_file_line: Lines of the configuratin file * * Returns the number of bytes printed. */ int profile_printall(const gchar **swtpm_prg_l, gchar *const *config_file_lines) { g_autoptr(JsonGenerator) jg = NULL; g_autoptr(JsonObject) jo = NULL; g_autoptr(JsonNode) root = NULL; g_autofree gchar *out = NULL; JsonArray *ja; jo = json_object_new(); ja = profile_gather_local(config_file_lines); if (ja) json_object_set_array_member(jo, "local", ja); ja = profile_gather_dir(DISTRO_PROFILES_DIR); if (ja) json_object_set_array_member(jo, "distro", ja); ja = profile_gather_builtin(swtpm_prg_l); if (ja) json_object_set_array_member(jo, "builtin", ja); root = json_node_new(JSON_NODE_OBJECT); json_node_set_object(root, jo); jg = json_generator_new(); json_generator_set_root(jg, root); out = json_generator_to_data(jg, NULL); return printf("%s\n", out); }