diff --git a/src/swtpm/Makefile.am b/src/swtpm/Makefile.am index 6eedf1635..d3276b4ba 100644 --- a/src/swtpm/Makefile.am +++ b/src/swtpm/Makefile.am @@ -21,6 +21,7 @@ noinst_HEADERS = \ options.h \ daemonize.h \ pidfile.h \ + profile.h \ seccomp_profile.h \ server.h \ swtpm_aes.h \ @@ -49,6 +50,7 @@ libswtpm_libtpms_la_SOURCES = \ mainloop.c \ options.c \ pidfile.c \ + profile.c \ seccomp_profile.c \ server.c \ swtpm_aes.c \ diff --git a/src/swtpm/check_algos.c b/src/swtpm/check_algos.c index 7716db92d..8278ff36f 100644 --- a/src/swtpm/check_algos.c +++ b/src/swtpm/check_algos.c @@ -37,6 +37,8 @@ #include "config.h" +#define _GNU_SOURCE +#include #include #include "check_algos.h" @@ -404,6 +406,24 @@ static const struct algorithms_tests { } }; +static const struct fips_disabled { + const char *name; + ssize_t name_len; // -1 for full name, prefix matching otherwise +} ossl_fips_disabled_algorithms[] = { +#define ENTRY(NAME, NAME_LEN) { .name = NAME, .name_len = NAME_LEN, } + ENTRY("camellia", -1), + ENTRY("camellia-min-size=", 18), + ENTRY("tdes", -1), + ENTRY("tdes-min-size=", 14), + ENTRY("rsaes", -1), + ENTRY("ecc-nist-p192", -1), + ENTRY("ecc-bn", -1), + ENTRY("ecc-bn-p", 8), // p256, p638 + ENTRY("ecc-sm2-p", 9), // p256 + ENTRY(NULL, 0) +#undef ENTRY +}; + /* list of minimum required key sizes for FIPS */ static const struct key_sizes { const char **names; // all of these must be found enabled in profile @@ -515,3 +535,53 @@ unsigned int ossl_algorithms_are_disabled(const gchar *const*algorithms, disabled_filter, stop_on_first_disabled); } + +/* + * Remove those algorithms in the given array that are disabled by FIPS and + * set or adjust key lengths to minimum required sizes for FIPS. + */ +int ossl_remove_fips_disabled_algorithms(gchar ***algorithms) +{ + unsigned long v; + size_t i, l; + gchar *old; + ssize_t j; + + /* remove all unsupported algorithms */ + for (i = 0; ossl_fips_disabled_algorithms[i].name != NULL; i++) + strv_remove(*algorithms, + ossl_fips_disabled_algorithms[i].name, + ossl_fips_disabled_algorithms[i].name_len); + + /* set/adjust min. key sizes */ + for (i = 0; fips_key_sizes[i].keyword; i++) { + l = strlen(fips_key_sizes[i].keyword); + j = strv_strncmp((const gchar *const*)*algorithms, fips_key_sizes[i].keyword, l); + if (j >= 0) { + /* key size large enough as indicated? */ + v = strtoul(&((*algorithms)[j])[l], NULL, 10); + if (v >= fips_key_sizes[i].min_size) + continue; + + /* need to adjust min key size */ + old = (*algorithms)[j]; + } else { + /* append to strv */ + j = g_strv_length(*algorithms); + *algorithms = g_realloc(*algorithms, + sizeof(char *) * (j + 1 + 1)); + (*algorithms)[j + 1] = NULL; + old = NULL; + } + + if (asprintf(&((*algorithms)[j]), "%s%u", + fips_key_sizes[i].keyword, + fips_key_sizes[i].min_size) < 0) { + (*algorithms)[j] = old; + return 1; + } + g_free(old); + } + + return 0; +} diff --git a/src/swtpm/check_algos.h b/src/swtpm/check_algos.h index 37e427a07..4b2075298 100644 --- a/src/swtpm/check_algos.h +++ b/src/swtpm/check_algos.h @@ -56,4 +56,6 @@ unsigned int ossl_algorithms_are_disabled(const gchar *const*algorithms, #define FIX_ENABLE_SHA1_SIGNATURES (1 << 1) /* fix by setting OPENSSL_ENABLE_SHA1_SIGNATURES=1 */ #define FIX_DISABLE_CONFIG (1 << 2) /* fix by modifying openssl config (how?) */ +int ossl_remove_fips_disabled_algorithms(gchar ***algorithms); + #endif /* _SWTPM_CHECK_ALGOS_H_ */ diff --git a/src/swtpm/common.c b/src/swtpm/common.c index 4a6437416..df510e225 100644 --- a/src/swtpm/common.c +++ b/src/swtpm/common.c @@ -71,6 +71,8 @@ #include "seccomp_profile.h" #include "tpmlib.h" #include "mainloop.h" +#include "profile.h" +#include "swtpm_utils.h" /* --log %s */ static const OptionDesc logging_opt_desc[] = { @@ -1458,6 +1460,10 @@ static int parse_profile_options(char *options, char **json_profile) } } + if (option_get_bool(ovs, "remove-fips-disabled", false) && + profile_remove_fips_disabled_algorithms(json_profile) == 1) + goto error; + option_values_free(ovs); return 0; @@ -1467,6 +1473,7 @@ static int parse_profile_options(char *options, char **json_profile) "Out of memory to create JSON profile\n"); error: + SWTPM_G_FREE(*json_profile); option_values_free(ovs); free(error); diff --git a/src/swtpm/cuse_tpm.c b/src/swtpm/cuse_tpm.c index fb36c3516..085768bf1 100644 --- a/src/swtpm/cuse_tpm.c +++ b/src/swtpm/cuse_tpm.c @@ -277,8 +277,10 @@ static const char *usage = " releases the storage lock on outgoing migration\n" "--print-capabilities : print capabilities and terminate\n" "--print-states : print existing TPM states and terminate\n" -"--profile name=|profile=\n" +"--profile name=|profile=[,remove-fips-disabled]\n" " : Set a profile on the TPM 2\n" +" remove-fips-disabled: On the 'custom' profile remove all\n" +" algorithms disabled by FIPS in OpenSSL\n" "--print-profiles\n" " : print all profiles supported by libtpms\n" "-h|--help : display this help screen and terminate\n" diff --git a/src/swtpm/profile.c b/src/swtpm/profile.c new file mode 100644 index 000000000..b654c7220 --- /dev/null +++ b/src/swtpm/profile.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +/* + * profile.c: Functions for handling profiles + * + * Author: Stefan Berger, stefanb@linux.ibm.com + * + * Copyright (c) IBM Corporation, 2024 + */ + +#include "config.h" + +#include + +#include "profile.h" +#include "utils.h" +#include "swtpm_utils.h" +#include "check_algos.h" + +/* + * Remove algorithms disabled by FIPS from the given JSON profile but only do + * this if this is the 'custom' profile. + * + * @json_profile: Pointer to the string with the JSON profile + * + * Return values: + * 0 : no error + * 1 : fatal error + * 2 : this is not the 'custom' profile + */ +int profile_remove_fips_disabled_algorithms(char **json_profile) +{ + g_autofree gchar *info_data = NULL; + g_autofree gchar *value = NULL; + g_auto(GStrv) algorithms = NULL; + int ret; + + ret = json_get_map_key_value(*json_profile, "Name", &value); + if (ret || !value || strcmp(value, "custom")) + return 2; + + SWTPM_G_FREE(value); + ret = json_get_map_key_value(*json_profile, "Algorithms", &value); + if (ret == 1) + return 1; + + if (ret == 2) { + info_data = TPMLIB_GetInfo(TPMLIB_INFO_RUNTIME_ALGORITHMS); + + ret = json_get_submap_value(info_data, "RuntimeAlgorithms", "Implemented", + &value); + if (ret) + return 1; + } + algorithms = g_strsplit(value, ",", -1); + if (ossl_remove_fips_disabled_algorithms(&algorithms)) + return 1; + + g_free(value); + value = g_strjoinv(",", algorithms); + + /* put algorithms into JSON */ + ret = json_set_map_key_value(json_profile, "Algorithms", value); + if (ret) + return 1; + + return 0; +} diff --git a/src/swtpm/profile.h b/src/swtpm/profile.h new file mode 100644 index 000000000..5364a3d5e --- /dev/null +++ b/src/swtpm/profile.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +/* + * profile.h: Header for profile.c + * + * Author: Stefan Berger, stefanb@linux.ibm.com + * + * Copyright (c) IBM Corporation, 2024 + */ + +#ifndef _SWTPM_PROFILE_H_ +#define _SWTPM_PROFILE_H_ + +int profile_remove_fips_disabled_algorithms(char **json_profile); + +#endif /* _SWTPM_PROFILE_H_ */ diff --git a/src/swtpm/swtpm.c b/src/swtpm/swtpm.c index d87b5e41f..e094a5ed0 100644 --- a/src/swtpm/swtpm.c +++ b/src/swtpm/swtpm.c @@ -197,8 +197,10 @@ static void usage(FILE *file, const char *prgname, const char *iface) " : print capabilities and terminate\n" "--print-states\n" " : print existing TPM states and terminate\n" - "--profile name=|profile=\n" + "--profile name=|profile=[,remove-fips-disabled]\n" " : Set a profile on the TPM 2\n" + " remove-fips-disabled: On the 'custom' profile remove all\n" + " algorithms disabled by FIPS in OpenSSL\n" "--print-profiles\n" " : print all profiles supported by libtpms\n" "-h|--help : display this help screen and terminate\n" diff --git a/src/swtpm/swtpm_chardev.c b/src/swtpm/swtpm_chardev.c index fe857b149..f5718a235 100644 --- a/src/swtpm/swtpm_chardev.c +++ b/src/swtpm/swtpm_chardev.c @@ -218,8 +218,10 @@ static void usage(FILE *file, const char *prgname, const char *iface) " : print capabilities and terminate\n" "--print-states\n" " : print existing TPM states and terminate\n" - "--profile name=|profile=\n" + "--profile name=|profile=[,remove-fips-disabled]\n" " : Set a profile on the TPM 2\n" + " remove-fips-disabled: On the 'custom' profile remove all\n" + " algorithms disabled by FIPS in OpenSSL\n" "--print-profiles\n" " : print all profiles supported by libtpms\n" "-h|--help : display this help screen and terminate\n" diff --git a/src/swtpm/utils.c b/src/swtpm/utils.c index 928be58f5..d1f61f32d 100644 --- a/src/swtpm/utils.c +++ b/src/swtpm/utils.c @@ -349,6 +349,85 @@ ssize_t read_eintr(int fd, void *buffer, size_t buflen) } } +/* + * Get the value of a map's key. + * + * Returns: + * 0 : success + * 1 : failure to parse the JSON input + * 2 : could not find the key + */ +int json_get_map_key_value(const char *json_input, + const char *key, char **value) +{ + g_autoptr(GError) error = NULL; + g_autoptr(JsonParser) jp = NULL; + g_autoptr(JsonReader) jr = NULL; + JsonNode *root; + + jp = json_parser_new(); + if (!json_parser_load_from_data(jp, json_input, -1, &error)) { + logprintf(STDERR_FILENO, + "Could not parse JSON '%s': %s\n", json_input, error->message); + return 1; + } + + root = json_parser_get_root(jp); + jr = json_reader_new(root); + + if (!json_reader_read_member(jr, key)) + return 2; + + *value = g_strdup(json_reader_get_string_value(jr)); + if (*value == NULL) { + /* value not a string */ + logprintf(STDERR_FILENO, + "'%s' in JSON map is not a string\n", key); + return 1; + } + + return 0; +} + +/* + * Set the value of a map's key and return the new string + * + * Returns: + * 0 : success + * 1 : fatal failure + */ +int json_set_map_key_value(char **json_string, + const char *key, const char *value) +{ + g_autoptr(JsonParser) jp = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(JsonGenerator) jg; + JsonObject *jo; + JsonNode *root; + + jg = json_generator_new(); + if (!jg) + return 1; + + jp = json_parser_new(); + if (!json_parser_load_from_data(jp, *json_string, -1, &error)) { + logprintf(STDERR_FILENO, + "Could not parse JSON '%s': %s\n", *json_string, error->message); + return 1; + } + + root = json_parser_get_root(jp); + json_generator_set_root(jg, root); + + jo = json_node_get_object(root); + json_object_set_string_member(jo, key, value); + + g_free(*json_string); + *json_string = json_generator_to_data(jg, NULL); + + return 0; +} + /* * In the given JSON map find a map with name @field_name and then * access the field @field_name2 in this map and return its value. @@ -440,3 +519,32 @@ gboolean strv_contains_all(const gchar *const*haystack, const gchar *const*needl } return true; } + +/* + * Remove all entries in the @array that either fully match @toremove + * (@len = -1) or where @toremove is a prefix of. + * This function returns the number of entries that were removed. + */ +gboolean strv_remove(gchar **array, const gchar *toremove, ssize_t len) +{ + unsigned int num = 0; + size_t i = 0, j; + + while (array[i]) { + if ((len < 0 && strcmp(array[i], toremove) == 0) || + (strncmp(array[i], toremove, len) == 0)) { + g_free(array[i]); + + j = i; + do { + j++; + array[j - 1] = array[j]; + } while(array[j]); + + num++; + } else { + i++; + } + } + return num; +} diff --git a/src/swtpm/utils.h b/src/swtpm/utils.h index e50ea0357..902deff5d 100644 --- a/src/swtpm/utils.h +++ b/src/swtpm/utils.h @@ -77,9 +77,15 @@ ssize_t read_eintr(int fd, void *buffer, size_t buflen); int json_get_submap_value(const char *json_input, const char *field_name, const char *field_name2, char **value); +int json_get_map_key_value(const char *json_input, + const char *key, char **value); +int json_set_map_key_value(char **json_input, + const char *key, const char *value); ssize_t strv_strncmp(const gchar *const*str_array, const gchar *s, size_t n); gboolean strv_contains_all(const gchar *const*haystack, const gchar *const*needles); +gboolean strv_remove(gchar **array, const gchar *toremove, ssize_t len); + #endif /* _SWTPM_UTILS_H_ */