diff --git a/man/man8/swtpm_setup.pod b/man/man8/swtpm_setup.pod index 636e35d..5559f2e 100644 --- a/man/man8/swtpm_setup.pod +++ b/man/man8/swtpm_setup.pod @@ -193,6 +193,31 @@ size is used. This option allows the reconfiguration of the active PCR banks of a TPM 2 using the I<--pcr-banks> option. +=item B<--profile > + +Configure a TPM 2 with the given profile. Example profiles look +like this: + + {"Name": "null"} + + {"Name": "default-v1"} + + { + "Name": "custom", + "Algorithms":"rsa,rsa-min-size=1024,tdes-min-size=128,hmac,aes,\ + aes-min-size=128,mgf1,keyedhash,xor,sha256,sha384,\ + sha512,null,rsassa,rsaes,rsapss,oaep,ecdsa,ecdh,ecdaa,\ + sm2,ecschnorr,ecmqv,kdf1-sp800-56a,kdf2,kdf1-sp800-108,\ + ecc,ecc-min-size=192,ecc-nist,ecc-bn,symcipher,camellia,\ + camellia-min-size=128,cmac,ctr,ofb,cbc,cfb,ecb" + } + + +The JSON profile must contain the 'name' field with a name of a profile +supported by libtpms. The profile may contain an algorithms field with a +list of algorithms to enable. Unknown fields in the JSON profile will be +ignored. + =item B<--print-capabilities> (since v0.2) Print capabilities that were added to swtpm_setup after version 0.1. diff --git a/src/swtpm/capabilities.c b/src/swtpm/capabilities.c index 8c57e51..646acb8 100644 --- a/src/swtpm/capabilities.c +++ b/src/swtpm/capabilities.c @@ -117,16 +117,54 @@ oom: goto cleanup; } +static int get_profile_data(const char *info_data, JsonReader *jr, + GString *gstr, const char *member, + const char *prefix, const char *suffix) +{ + g_string_append_printf(gstr, "%s: { \"canbedisabled\": ", prefix); + + if (!json_reader_read_member(jr, member)) { + logprintf(STDERR_FILENO, + "Missing '%s' field: %s\n", + member, info_data); + return -1; + } + if (!json_reader_read_member(jr, "CanBeDisabled")) { + logprintf(STDERR_FILENO, + "Missing 'CanBeDisabled' field under '%s': %s\n", + member, info_data); + return -1; + } + g_string_append_printf(gstr, "\"%s\", \"implemented\": ", + json_reader_get_string_value(jr)); + json_reader_end_member(jr); + if (!json_reader_read_member(jr, "Implemented")) { + logprintf(STDERR_FILENO, + "Missing 'Implemented' field under '%s': %s\n", + member, info_data); + return -1; + } + g_string_append_printf(gstr, "\"%s\" }%s", + json_reader_get_string_value(jr), + suffix); + json_reader_end_member(jr); + json_reader_end_member(jr); + + return 0; +} + static int get_profiles(gchar **profiles) { - char *info_data = TPMLIB_GetInfo(TPMLIB_INFO_AVAILABLE_PROFILES); + char *info_data = TPMLIB_GetInfo(TPMLIB_INFO_AVAILABLE_PROFILES | + TPMLIB_INFO_RUNTIME_ALGORITHMS | + TPMLIB_INFO_RUNTIME_COMMANDS); JsonParser *jp = NULL; JsonReader *jr = NULL; g_autoptr(GError) error = NULL; JsonNode *root; gint i, num; int ret = 0; - GString *gstr = g_string_new(NULL); + GString *gstr = g_string_new("\"names\": [ "); jp = json_parser_new(); @@ -155,11 +193,18 @@ static int get_profiles(gchar **profiles) goto error_unref_jr; } g_string_append_printf(gstr, "%s\"%s\"", - i > 0 ? ", " : " ", + i > 0 ? ", " : "", json_reader_get_string_value(jr)); json_reader_end_element(jr); json_reader_end_element(jr); } + json_reader_end_member(jr); + + if (get_profile_data(info_data, jr, gstr, + "RuntimeAlgorithms", " ], \"algorithms\"", "") || + get_profile_data(info_data, jr, gstr, + "RuntimeCommands", ", \"commands\"", " ")) + goto error_unref_jr; error_unref_jr: @@ -218,7 +263,7 @@ int capabilities_print_json(bool cusetpm, TPMLIB_TPMVersion tpmversion) "\"features\": [ " "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" " ], " - "\"profiles\": [%s ], " + "\"profiles\": { %s}, " "\"version\": \"" VERSION "\" " "}", with_tpm1, diff --git a/src/swtpm_setup/Makefile.am b/src/swtpm_setup/Makefile.am index c0f916b..1a8ce08 100644 --- a/src/swtpm_setup/Makefile.am +++ b/src/swtpm_setup/Makefile.am @@ -8,6 +8,7 @@ MY_CFLAGS = @MY_CFLAGS@ MY_LDFLAGS = @MY_LDFLAGS@ noinst_HEADERS = \ + profile.h \ swtpm.h \ swtpm_setup.h \ swtpm_setup_utils.h @@ -16,6 +17,7 @@ bin_PROGRAMS = \ swtpm_setup swtpm_setup_SOURCES = \ + profile.c \ swtpm.c \ swtpm_setup.c \ swtpm_setup_utils.c \ diff --git a/src/swtpm_setup/profile.c b/src/swtpm_setup/profile.c new file mode 100644 index 0000000..9e5c63c --- /dev/null +++ b/src/swtpm_setup/profile.c @@ -0,0 +1,108 @@ +/* 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 +#include "config.h" + +#include + +#include "profile.h" +#include "swtpm_utils.h" + +/* Return the names of the supported profiles */ +static +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); + if (ret) + return ret; + + 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; +} diff --git a/src/swtpm_setup/profile.h b/src/swtpm_setup/profile.h new file mode 100644 index 0000000..ac23531 --- /dev/null +++ b/src/swtpm_setup/profile.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * profile.h: TPM 2 profile handling + * + * Author: Stefan Berger, stefanb@linux.ibm.com + * + * Copyright (c) IBM Corporation, 2022 + */ + +#ifndef SWTPM_SETUP_PROFILE_H +#define SWTPM_SETUP_PROFILE_H + +#include + +int check_json_profile(const gchar *swtpm_capabilities_json, const char *json_profile); + +#endif /* SWTPM_SETUP_PROFILE_H */ diff --git a/src/swtpm_setup/swtpm.c b/src/swtpm_setup/swtpm.c index ddbc41d..5fd63d0 100644 --- a/src/swtpm_setup/swtpm.c +++ b/src/swtpm_setup/swtpm.c @@ -76,6 +76,7 @@ static void swtpm_close_comm(struct swtpm *self, bool all) static int swtpm_start(struct swtpm *self) { g_autofree gchar *tpmstate = g_strdup_printf("backend-uri=%s", self->state_path); + g_autofree gchar *json_profile = NULL; g_autofree gchar *pidfile_arg = NULL; g_autofree gchar *server_fd = NULL; g_autofree gchar *ctrl_fd = NULL; @@ -118,6 +119,12 @@ static int swtpm_start(struct swtpm *self) argv = concat_arrays(argv, (gchar*[]){"--key", keyopts, NULL}, TRUE); } + if (self->json_profile != NULL) { + json_profile = g_strdup_printf("profile=%s", self->json_profile); + argv = concat_arrays(argv, (gchar*[]){"--profile", json_profile, NULL}, TRUE); + logit(self->logfile, "Apply profile: %s\n", self->json_profile); + } + if (gl_LOGFILE != NULL) { logop = g_strdup_printf("file=%s", gl_LOGFILE); argv = concat_arrays(argv, (gchar*[]){"--log", logop, NULL}, TRUE); @@ -2009,7 +2016,7 @@ static void swtpm_init(struct swtpm *swtpm, gchar **swtpm_exec_l, const gchar *state_path, const gchar *keyopts, const gchar *logfile, int *fds_to_pass, size_t n_fds_to_pass, - gboolean is_tpm2) + gboolean is_tpm2, const gchar *json_profile) { swtpm->cops = &swtpm_cops; swtpm->swtpm_exec_l = swtpm_exec_l; @@ -2019,6 +2026,7 @@ static void swtpm_init(struct swtpm *swtpm, swtpm->fds_to_pass = fds_to_pass; swtpm->n_fds_to_pass = n_fds_to_pass; swtpm->is_tpm2 = is_tpm2; + swtpm->json_profile = json_profile; swtpm->pid = -1; swtpm->ctrl_fds[0] = swtpm->ctrl_fds[1] = -1; @@ -2032,7 +2040,7 @@ struct swtpm12 *swtpm12_new(gchar **swtpm_exec_l, const gchar *state_path, struct swtpm12 *swtpm12 = g_malloc0(sizeof(struct swtpm12)); swtpm_init(&swtpm12->swtpm, swtpm_exec_l, state_path, keyopts, logfile, - fds_to_pass, n_fds_to_pass, FALSE); + fds_to_pass, n_fds_to_pass, FALSE, NULL); swtpm12->ops = &swtpm_tpm12_ops; return swtpm12; @@ -2040,12 +2048,13 @@ struct swtpm12 *swtpm12_new(gchar **swtpm_exec_l, const gchar *state_path, struct swtpm2 *swtpm2_new(gchar **swtpm_exec_l, const gchar *state_path, const gchar *keyopts, const gchar *logfile, - int *fds_to_pass, size_t n_fds_to_pass) + int *fds_to_pass, size_t n_fds_to_pass, + const gchar *json_profile) { struct swtpm2 *swtpm2 = g_malloc0(sizeof(struct swtpm2)); swtpm_init(&swtpm2->swtpm, swtpm_exec_l, state_path, keyopts, logfile, - fds_to_pass, n_fds_to_pass, TRUE); + fds_to_pass, n_fds_to_pass, TRUE, json_profile); swtpm2->ops = &swtpm_tpm2_ops; return swtpm2; diff --git a/src/swtpm_setup/swtpm.h b/src/swtpm_setup/swtpm.h index 2f81dce..76c42fd 100644 --- a/src/swtpm_setup/swtpm.h +++ b/src/swtpm_setup/swtpm.h @@ -68,6 +68,7 @@ struct swtpm { const int *fds_to_pass; size_t n_fds_to_pass; gboolean is_tpm2; + const char *json_profile; GPid pid; int ctrl_fds[2]; @@ -90,7 +91,8 @@ struct swtpm12 *swtpm12_new(gchar **swtpm_prg_l, const gchar *tpm_state_path, struct swtpm2 *swtpm2_new(gchar **swtpm_prg_l, const gchar *tpm_state_path, const gchar *swtpm_keyopts, const gchar *logfile, - int *fds_to_pass, size_t n_fds_to_pass); + int *fds_to_pass, size_t n_fds_to_pass, + const gchar *profile_rules); void swtpm_free(struct swtpm *); diff --git a/src/swtpm_setup/swtpm_setup.c b/src/swtpm_setup/swtpm_setup.c index eaefd31..1272a37 100644 --- a/src/swtpm_setup/swtpm_setup.c +++ b/src/swtpm_setup/swtpm_setup.c @@ -32,6 +32,7 @@ #include +#include "profile.h" #include "swtpm.h" #include "swtpm_conf.h" #include "swtpm_utils.h" @@ -548,14 +549,14 @@ static int init_tpm2(unsigned long flags, gchar **swtpm_prg_l, const gchar *conf const gchar *tpm2_state_path, const gchar *vmid, const gchar *pcr_banks, const gchar *swtpm_keyopt, int *fds_to_pass, size_t n_fds_to_pass, unsigned int rsa_keysize, const gchar *certsdir, - const gchar *user_certsdir) + const gchar *user_certsdir, const gchar *json_profile) { struct swtpm2 *swtpm2; struct swtpm *swtpm; int ret; swtpm2 = swtpm2_new(swtpm_prg_l, tpm2_state_path, swtpm_keyopt, gl_LOGFILE, - fds_to_pass, n_fds_to_pass); + fds_to_pass, n_fds_to_pass, json_profile); if (swtpm2 == NULL) return 1; swtpm = &swtpm2->swtpm; @@ -978,6 +979,9 @@ static void usage(const char *prgname, const char *default_config_file) " The active PCR banks can be changed but no new keys will\n" " be created.\n" "\n" + "--profile \n" + " : Configure swtpm with the given profile.\n" + "\n" "--version : Display version and exit\n" "\n" "--help,-h : Display this help screen\n\n", @@ -1098,6 +1102,18 @@ static int get_rsa_keysize_caps(unsigned long flags, gchar **swtpm_prg_l, return 0; } +static int validate_json_profile(gchar **swtpm_prg_l, const char *json_profile) +{ + g_autofree gchar *standard_output = NULL; + int ret; + + ret = get_swtpm_capabilities(swtpm_prg_l, TRUE, &standard_output); + if (ret) + return ret; + + return check_json_profile(standard_output, json_profile); +} + /* Print the JSON object of swtpm_setup's capabilities */ static int print_capabilities(char **swtpm_prg_l, gboolean swtpm_has_tpm12, gboolean swtpm_has_tpm2) @@ -1122,7 +1138,10 @@ static int print_capabilities(char **swtpm_prg_l, gboolean swtpm_has_tpm12, "\"features\": [ %s%s\"cmdarg-keyfile-fd\", \"cmdarg-pwdfile-fd\", \"tpm12-not-need-root\"" ", \"cmdarg-write-ek-cert-files\", \"cmdarg-create-config-files\"" ", \"cmdarg-reconfigure-pcr-banks\"" - "%s ], " + "%s" + ", \"cmdarg-profile\"" + "" + " ], " "\"version\": \"" VERSION "\" " "}\n", swtpm_has_tpm12 ? "\"tpm-1.2\", " : "", @@ -1244,6 +1263,7 @@ int main(int argc, char *argv[]) {"version", no_argument, NULL, '1'}, {"print-capabilities", no_argument, NULL, 'y'}, {"reconfigure", no_argument, NULL, 'R'}, + {"profile", required_argument, NULL, 'I'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; @@ -1272,6 +1292,7 @@ int main(int argc, char *argv[]) g_autofree gchar *runas = NULL; g_autofree gchar *certsdir = NULL; g_autofree gchar *user_certsdir = NULL; + g_autofree gchar *json_profile = NULL; gchar *tmp; gchar **swtpm_prg_l = NULL; gchar **tmp_l = NULL; @@ -1457,6 +1478,10 @@ int main(int argc, char *argv[]) case 'R': /* --reconfigure */ flags |= SETUP_RECONFIGURE_F; break; + case 'I': /* --profile */ + g_free(json_profile); + json_profile = g_strdup(optarg); + break; case '?': case 'h': /* --help */ usage(argv[0], config_file); @@ -1484,7 +1509,6 @@ int main(int argc, char *argv[]) } g_free(tmp); - ret = get_supported_tpm_versions(swtpm_prg_l, &swtpm_has_tpm12, &swtpm_has_tpm2); if (ret != 0) goto error; @@ -1615,6 +1639,14 @@ int main(int argc, char *argv[]) pcr_banks = get_default_pcr_banks(config_file_lines); } + if ((flags & SETUP_TPM2_F) != 0 && json_profile) { + if (validate_json_profile(swtpm_prg_l, json_profile) != 0) + goto error; + } else if (json_profile) { + logerr(gl_LOGFILE, "There's no --profile support for TPM 1.2\n"); + goto error; + } + if (cipher != NULL) { if (strcmp(cipher, "aes-128-cbc") != 0 && strcmp(cipher, "aes-cbc") != 0 && @@ -1729,7 +1761,7 @@ int main(int argc, char *argv[]) } else { ret = init_tpm2(flags, swtpm_prg_l, config_file, tpm_state_path, vmid, pcr_banks, swtpm_keyopt, fds_to_pass, n_fds_to_pass, rsa_keysize, certsdir, - user_certsdir); + user_certsdir, json_profile); } if (ret == 0) { diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index 2e8b14a..c3bf4b8 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -17,11 +17,13 @@ libswtpm_utils_la_CFLAGS = \ $(MY_CFLAGS) \ $(CFLAGS) \ $(HARDENING_CFLAGS) \ - $(GLIB_CFLAGS) + $(GLIB_CFLAGS) \ + $(JSON_GLIB_CFLAGS) libswtpm_utils_la_LDFLAGS = \ $(MY_LDFLAGS) \ - $(HARDENING_LDFLAGS) + $(HARDENING_LDFLAGS) \ + $(JSON_GLIB_LIBS) libswtpm_utils_la_SOURCES = \ swtpm_utils.c diff --git a/src/utils/swtpm_utils.c b/src/utils/swtpm_utils.c index 35a4243..95b7f50 100644 --- a/src/utils/swtpm_utils.c +++ b/src/utils/swtpm_utils.c @@ -21,6 +21,7 @@ #include #include +#include #include "swtpm_utils.h" @@ -440,3 +441,51 @@ int check_directory_access(const gchar *directory, int mode, const struct passwd } return 0; } + +int json_get_map_value(const char *json_input, const char *field_name, + gchar **value) +{ + g_autoptr(GError) error = NULL; + JsonParser *jp = NULL; + JsonReader *jr = NULL; + JsonNode *root; + int ret = -1; + + jp = json_parser_new(); + if (!json_parser_load_from_data(jp, json_input, -1, &error)) { + logerr(gl_LOGFILE, + "Could not parse JSON '%s': %s\n", json_input, error->message); + goto error_unref_jp; + } + + root = json_parser_get_root(jp); + jr = json_reader_new(root); + + if (!json_reader_read_member(jr, field_name)) { + logerr(gl_LOGFILE, "Missing '%s' field in '%s'\n", + field_name, json_input); + goto error_unref_jr; + } + *value = g_strdup(json_reader_get_string_value(jr)); + + ret = 0; + +error_unref_jr: + g_object_unref(jr); + +error_unref_jp: + g_object_unref(jp); + + return ret; +} + +int strv_strcmp(gchar *const*str_array, const gchar *s) +{ + size_t i; + + for (i = 0; str_array[i]; i++) { + if (strcmp(str_array[i], s) == 0) + return (int)i; + } + return -1; +} diff --git a/src/utils/swtpm_utils.h b/src/utils/swtpm_utils.h index 22f1f4b..6202f65 100644 --- a/src/utils/swtpm_utils.h +++ b/src/utils/swtpm_utils.h @@ -55,4 +55,9 @@ gchar *str_replace(const char *in, const char *torep, const char *rep); int check_directory_access(const gchar *directory, int mode, const struct passwd *curr_user); +int json_get_map_value(const char *json_input, const char *field_name, + gchar **value); + +int strv_strcmp(gchar *const*str_array, const gchar *s); + #endif /* SWTPM_UTILS_H */