From c161c043927a007d3d6ae4ce37dfad3fbb64a1fc Mon Sep 17 00:00:00 2001 From: Ingo Franzki Date: Mon, 11 Mar 2024 15:17:17 +0100 Subject: [PATCH] zkey: Add integritytab and integritysetup commands for integrity volumes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commands 'zkey integritytab' generates /etc/integritytab entries for volumes of type INTEGRITY. The 'zkey integritysetup' generates integritysetup commands for formatting and opening volumes of type INTEGRITY. Signed-off-by: Ingo Franzki Reviewed-by: Finn Callies Signed-off-by: Jan Höppner --- zkey/keystore.c | 248 +++++++++++++++++++++++++++++++++++++++++++++++- zkey/keystore.h | 6 ++ zkey/zkey.1 | 135 ++++++++++++++++++++++++++ zkey/zkey.c | 111 +++++++++++++++++++++- 4 files changed, 495 insertions(+), 5 deletions(-) diff --git a/zkey/keystore.c b/zkey/keystore.c index a4263404..02a470c0 100644 --- a/zkey/keystore.c +++ b/zkey/keystore.c @@ -4543,8 +4543,8 @@ static int _keystore_execute_cmd(const char *cmd, return rc; } - struct crypt_info { + bool integrity; bool execute; bool batch_mode; const char *keyfile; @@ -4785,6 +4785,104 @@ static int _keystore_process_crypttab(struct keystore *UNUSED(keystore), return 0; } +/** + * Processing function for the integritysetup function. Builds a integritysetup + * command line and optionally executes it. + * + * @param[in] keystore the keystore (not used here) + * @param[in] volume the volume to mount + * @param[in] dmname the device mapper name + * @param[in] cipher_spec the cipher specification + * @param[in] key_file_name the key file name + * @param[in] key_file_size the size of the key file in bytes + * @param[in] sector_size the sector size in bytes or 0 if not specified + * @param[in] volume_type the volume type + * @param[in] passphrase_file the passphrase file name (can be NULL) + * @param[in] info processing info + * + * @returns 0 if successful, a negative errno value otherwise + */ +static int _keystore_process_integritysetup(struct keystore *keystore, + const char *volume, + const char *dmname, + const char *cipher_spec, + const char *key_file_name, + size_t key_file_size, + size_t sector_size, + const char *volume_type, + const char *UNUSED(passphrase_file), + struct crypt_info *info) +{ + char temp[100]; + int rc = 0; + char *cmd; + + if (strcasecmp(volume_type, VOLUME_TYPE_INTEGRITY) != 0) + return 0; + + sprintf(temp, "--sector-size %lu ", sector_size); + + util_asprintf(&cmd, + "integritysetup %s %s%s--integrity %s " + "--integrity-key-file '%s' --integrity-key-size %lu " + "%s%s%s%s", + info->open ? "open" : "format", + info->batch_mode ? "-q " : "", + keystore->verbose ? "-v " : "", + cipher_spec, key_file_name, key_file_size, + sector_size > 0 && !info->open ? temp : "", + volume, info->open ? " " : "", + info->open ? dmname : ""); + + if (info->execute) { + printf("Executing: %s\n", cmd); + rc = _keystore_execute_cmd(cmd, "integritysetup"); + } else { + printf("%s\n", cmd); + } + + free(cmd); + return rc; +} + +/** + * Processing function for the integritytab function. Builds a integritytab + * entry and prints it. + * + * @param[in] keystore the keystore (not used here) + * @param[in] volume the volume to mount + * @param[in] dmname the device mapper name + * @param[in] cipher_spec the cipher specification + * @param[in] key_file_name the key file name + * @param[in] key_file_size the size of the key file in bytes + * @param[in] sector_size the sector size in bytes or 0 if not specified + * @param[in] volume_type the volume type + * @param[in] passphrase_file the passphrase file name (can be NULL) + * @param[in] info processing info (not used here) + * + * @returns 0 if successful, a negative errno value otherwise + */ + +static int _keystore_process_integritytab(struct keystore *UNUSED(keystore), + const char *volume, + const char *dmname, + const char *cipher_spec, + const char *key_file_name, + size_t UNUSED(key_file_size), + size_t UNUSED(sector_size), + const char *volume_type, + const char *UNUSED(passphrase_file), + struct crypt_info *UNUSED(info)) +{ + if (strcasecmp(volume_type, VOLUME_TYPE_INTEGRITY) != 0) + return 0; + + printf("%s\t%s\t%s\tintegrity-algorithm=%s\n", volume, dmname, + key_file_name, cipher_spec); + + return 0; +} + /** * Builds a cipher specification for cryptsetup/crypttab * @@ -4820,6 +4918,39 @@ out: return cipher_spec; } +/** + * Builds a hmac specification for integritysetup/integritytab + * + * @param properties the key properties + * + * @returns the hmac spec string (must be freed by the caller) + */ +static char *_keystore_build_hmac_spec(struct properties *properties, + size_t bitsize) +{ + char *cipher_spec = NULL; + char *cipher = NULL; + char *digest = NULL; + + cipher = properties_get(properties, PROP_NAME_CIPHER); + if (cipher == NULL) + goto out; + + digest = properties_get(properties, PROP_NAME_DIGEST); + if (digest == NULL) + goto out; + + util_asprintf(&cipher_spec, "%s-%s%u", cipher, digest, bitsize / 2); + +out: + if (cipher != NULL) + free(cipher); + if (digest != NULL) + free(digest); + + return cipher_spec; +} + /** * Processing function for the cryptsetup and crypttab functions. * Extracts the required information and calls the secondary processing function @@ -4847,6 +4978,7 @@ static int _keystore_process_crypt(struct keystore *keystore, size_t sector_size = 0; char *volumes = NULL; u8 *secure_key = NULL; + size_t bitsize = 0; char *dmname; char *temp; int rc = 0; @@ -4859,9 +4991,16 @@ static int _keystore_process_crypt(struct keystore *keystore, if (secure_key == NULL) return -EIO; - cipher_spec = _keystore_build_cipher_spec(properties, - is_xts_key(secure_key, - secure_key_size)); + if (info->integrity) { + rc = get_key_bit_size(secure_key, secure_key_size, &bitsize); + if (rc != 0) + goto out; + cipher_spec = _keystore_build_hmac_spec(properties, bitsize); + } else { + cipher_spec = _keystore_build_cipher_spec(properties, + is_xts_key(secure_key, + secure_key_size)); + } if (cipher_spec == NULL) { rc = -EINVAL; goto out; @@ -4965,6 +5104,7 @@ int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, util_file_read_i(&info.fips, 10, "/proc/sys/crypto/fips_enabled"); + info.integrity = false; info.execute = execute; info.open = open; info.format = format; @@ -5031,6 +5171,7 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, return -EINVAL; } + info.integrity = false; info.keyfile = keyfile; info.keyfile_offset = keyfile_offset; info.keyfile_size = keyfile_size; @@ -5054,6 +5195,105 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, return rc; } +/** + * Generates integritysetup commands for one or multiple volumes. + * + * @param[in] keystore the key store + * @param[in] volume_filter the volume filter. Can contain wild cards, and + * multiple volume filters separated by commas. + * The ':dm-name' part of the volume is optional + * for the volume filter. If not specified, the filter + * checks the volume part only. + * @param[in] execute If TRUE the integritysetup command is executed, + * otherwise it is printed to stdout + * @param[in] batch_mode If TRUE, suppress integritysetup confirmation + * questions + * @param[in] open If TRUE, generate integritysetup open commands + * @returns 0 for success or a negative errno in case of an error + */ +int keystore_integritysetup(struct keystore *keystore, + const char *volume_filter, + bool execute, bool batch_mode, bool open) +{ + struct crypt_info info = { 0 }; + int rc; + + util_assert(keystore != NULL, "Internal error: keystore is NULL"); + + if (volume_filter == NULL) + volume_filter = "*"; + + info.integrity = true; + info.execute = execute; + info.open = open; + info.batch_mode = batch_mode; + info.volume_filter = str_list_split(volume_filter); + info.process_func = _keystore_process_integritysetup; + + rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL, + VOLUME_TYPE_INTEGRITY, + KEY_TYPE_FILTER_HMAC, + false, false, + _keystore_process_crypt, &info); + + str_list_free_string_array(info.volume_filter); + + if (rc < 0) + pr_verbose(keystore, "Integritysetup failed with: %s", + strerror(-rc)); + else if (rc > 0) + pr_verbose(keystore, "Integritysetup failed with: %d", rc); + else + pr_verbose(keystore, + "Successfully generated integritysetup commands"); + + return rc; +} + +/** + * Generates integritytab entries for one or multiple volumes. + * + * @param[in] keystore the key store + * @param[in] volume_filter the volume filter. Can contain wild cards, and + * multiple volume filters separated by commas. + * The ':dm-name' part of the volume is optional + * for the volume filter. If not specified, the filter + * checks the volume part only. + * + * @returns 0 for success or a negative errno in case of an error + */ +int keystore_integritytab(struct keystore *keystore, const char *volume_filter) +{ + struct crypt_info info = { 0 }; + int rc; + + util_assert(keystore != NULL, "Internal error: keystore is NULL"); + + if (volume_filter == NULL) + volume_filter = "*"; + + info.integrity = true; + info.volume_filter = str_list_split(volume_filter); + info.process_func = _keystore_process_integritytab; + + rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL, + VOLUME_TYPE_INTEGRITY, + KEY_TYPE_FILTER_HMAC, + false, false, + _keystore_process_crypt, &info); + + str_list_free_string_array(info.volume_filter); + + if (rc != 0) + pr_verbose(keystore, "Integritysetup failed with: %s", + strerror(-rc)); + else + pr_verbose(keystore, + "Successfully generated integritysetup entries"); + + return rc; +} + /** * Converts a secure keys in the keystore * diff --git a/zkey/keystore.h b/zkey/keystore.h index e2cf7094..e8ee720c 100644 --- a/zkey/keystore.h +++ b/zkey/keystore.h @@ -130,6 +130,12 @@ int keystore_crypttab(struct keystore *keystore, const char *volume_filter, const char *volume_type, const char *keyfile, size_t keyfile_offset, size_t keyfile_size, size_t tries); +int keystore_integritysetup(struct keystore *keystore, + const char *volume_filter, + bool execute, bool batch_mode, bool open); + +int keystore_integritytab(struct keystore *keystore, const char *volume_filter); + int keystore_convert_key(struct keystore *keystore, const char *name, const char *key_type, bool noapqncheck, bool quiet, bool exportable, int pkey_fd, struct ext_lib *lib); diff --git a/zkey/zkey.1 b/zkey/zkey.1 index 9cfa29c1..10cfa3c0 100644 --- a/zkey/zkey.1 +++ b/zkey/zkey.1 @@ -735,6 +735,13 @@ options and .B \-\-keyfile\-size to control which part of the key file is used as passphrase. +.PP +.B Note: +Use the +.B integritytab +command to generate integritytab entries for volumes using the \fBintegrity\fP +volume type that are associated with secure HMAC keys contained in the +secure key repository. . .SS "Generate cryptsetup commands for volumes associated with secure AES keys" . @@ -811,6 +818,88 @@ To avoid cryptsetup confirmation questions, you can specify the .B \-\-batch\-mode option. These options are passed to the generated command(s) and behave in the same way as with \fBcryptsetup\fP. +.PP +.B Note: +Use the +.B integritysetup +command to generate \fBintegritysetup format\fP and \fBintegritysetup open\fP +commands for volumes using the \fBintegrity\fP volume type that are associated +with secure HMAC keys contained in the +secure key repository. +. +.SS "Generate integritytab entries for volumes associated with secure HMAC keys" +. +.B zkey +.BR integritytab | integrityt +.RB [ \-\-volumes | \-l +.IR volume1[:dmname1][,volume2[:dmname2][,...]] ] +.RB [ \-\-verbose | \-V ] +. +.PP +Use the +.B integritytab +command to generate integritytab entries using \fBdm\-integrity\fP for volumes +that are associated with secure HMAC keys contained in the secure key +repository. Only HMAC keys with a volume type of \fBintegrity\fP are considered. +Specify the +.B \-\-volumes +option to limit the list +of volumes where integritytab entries are generated for. You can use wildcards. +When wildcards are used you must quote the value. +The device-mapper name of an associated volume can be omitted; if it is +specified then only those volumes with the specified volume and device-mapper +name are selected. +.PP +.B Note: +Use the +.B crypttab +command to generate crypttab entries for volumes using the \fBplain\fP or +\fBluks2\fP volume type that are associated with secure AES keys contained in +the secure key repository. +. +.SS "Generate integritysetup commands for volumes associated with secure HMAC keys" +. +.B zkey +.BR integritysetup | integritys +.RB [ \-\-volumes | \-l +.IR volume1[:dmname1][,volume2[:dmname2][,...]] ] +.RB [ \-\-run | \-r ] +.RB [ \-\-open ] +.RB [ \-\-batch\-mode | \-q ] +.RB [ \-\-verbose | \-V ] +. +.PP +Use the +.B integritysetup +command to generate \fBintegritysetup format\fP, or \fBintegritysetup open\fP +commands for volumes that are associated with secure HMAC keys contained in the +secure key repository. Only HMAC keys with a volume type of \fBintegrity\fP are +considered. Specify the +.B \-\-volumes +option to limit the list +of volumes where integritysetup commands are generated for. You can use +wildcards. When wildcards are used you must quote the value. +The device-mapper name of an associated volume can be omitted; if it is +specified then only those volumes with the specified volume and device-mapper +name are selected. +Specify the +.B \-\-run +option to run the generated integritysetup commands. Specify the +.B \-\-open +to generate \fBintegritysetup open\fP commands. By default \fBintegritysetup +format\fP commands are generated. +To avoid integritysetup confirmation questions, you can specify the +.B \-\-batch\-mode +option. These option is passed to the generated command(s) and behave in the +same way as with \fBintegritysetup\fP. +.PP +.B Note: +Use the +.B cryptsetup +command to generate\fBcryptsetup plainOpen\fP, \fBcryptsetup luksOpen\fP, or +\fBcryptsetup luksFormat\fP commands for volumes using the \fBplain\fP or +\fBluks2\fP volume types that are associated with secure AES keys contained in +the secure key repository. . .SS "Convert existing AES or HMAC secure keys from one key type to another type" . @@ -1965,6 +2054,52 @@ generated cryptsetup command(s). . . . +.SS "Options for the integritytab command" +.TP +.BR \-l ", " \-\-volumes\~\fIvolume1[:dmname1][,volume2[:dmname2][,...]]\fP +Specifies a comma-separated list of volumes (block devices) which are +associated with secure HMAC keys in the repository. Only HMAC keys with a +volume type of \fBintegrity\fP are considered. +The volume association also contains the device-mapper name, separated by a +colon, used with \fBdm\-integrity\fP. You can omit the device-mapper name; if +it is specified then only those keys are selected that are associated with the +specified volume and device-mapper name. You can use wildcards to specify +the volumes and device-mapper names. +When wildcards are used you must quote the value. +This option is only used for secure keys contained in the secure key repository. +. +. +. +.SS "Options for the integritysetup command" +.TP +.BR \-l ", " \-\-volumes\~\fIvolume1[:dmname1][,volume2[:dmname2][,...]]\fP +Specifies a comma-separated list of volumes (block devices) which are +associated with secure HMAC keys in the repository. Only HMAC keys with a +volume type of \fBintegrity\fP are considered. +The volume association also contains the device-mapper name, separated by a +colon, used with \fBdm\-integrity\fP. You can omit the device-mapper name; if +it is specified then only those keys are selected that are associated with the +specified volume and device-mapper name. You can use wildcards to specify +the volumes and device-mapper names. +When wildcards are used you must quote the value. +This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-r ", " \-\-run +Runs the generated integritysetup commands. When one of the integritysetup +commands fail, no further integritysetup commands are run, and zkey ends with +an error. +This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-open +Generates \fBintegritysetup open\fP commands. By default \fBintegritysetup +format\fP commands are generated. +.TP +.BR \-q ", " \-\-batch\-mode +Suppress integritysetup confirmation questions. This option is passed to the +generated integritysetup command(s). +. +. +. .SS "Options for the convert command" .TP .BR \-N ", " \-\-name\~\fIkey\-name\fP diff --git a/zkey/zkey.c b/zkey/zkey.c index 53dc5b10..22bfc65d 100644 --- a/zkey/zkey.c +++ b/zkey/zkey.c @@ -134,6 +134,8 @@ static struct zkey_globals { #define COMMAND_COPY "copy " #define COMMAND_CRYPTTAB "crypttab" #define COMMAND_CRYPTSETUP "cryptsetup" +#define COMMAND_INTEGRITYTAB "integritytab" +#define COMMAND_INTEGRITYSETUP "integritysetup" #define COMMAND_CONVERT "convert" #define COMMAND_KMS "kms" #define COMMAND_KMS_PLUGINS "plugins" @@ -158,7 +160,7 @@ static struct zkey_globals { .command = OPT_COMMAND_PLACEHOLDER, \ } -#define ZKEY_COMMAND_MAX_LEN 10 +#define ZKEY_COMMAND_MAX_LEN 15 #define ENVVAR_ZKEY_REPOSITORY "ZKEY_REPOSITORY" #define DEFAULT_KEYSTORE "/etc/zkey/repository" @@ -970,6 +972,62 @@ static struct util_opt opt_vec[] = { .flags = UTIL_OPT_FLAG_NOSHORT, }, /***********************************************************/ + { + .flags = UTIL_OPT_FLAG_SECTION, + .desc = "OPTIONS", + .command = COMMAND_INTEGRITYTAB, + }, + { + .option = { "volumes", required_argument, NULL, 'l'}, + .argument = "VOLUME[:DMNAME][,...]", + .desc = "Comma-separated pairs of volume and device-mapper " + "names that are associated with the secure HMAC key in " + "the repository. Use this option to select the volumes " + "for which a integritytab entry is to be generated. " + "The device-mapper name (DMNAME) is optional. If " + "specified, only those volumes are selected where " + "both, the volume and the device-mapper name matches", + .command = COMMAND_INTEGRITYTAB, + }, + /***********************************************************/ + { + .flags = UTIL_OPT_FLAG_SECTION, + .desc = "OPTIONS", + .command = COMMAND_INTEGRITYSETUP, + }, + { + .option = { "volumes", required_argument, NULL, 'l'}, + .argument = "VOLUME[:DMNAME][,...]", + .desc = "Comma-separated pairs of volume and device-mapper " + "names that are associated with the secure HMAC key in " + "the repository. Use this option to select the volumes " + "for which a integritysetup command is to be generated " + "or run. The device-mapper name (DMNAME) is optional. " + "If specified, only those volumes are selected where " + "both, the volume and the device-mapper name matches", + .command = COMMAND_INTEGRITYSETUP, + }, + { + .option = {"run", 0, NULL, 'r'}, + .desc = "Runs the generated integritysetup command", + .command = COMMAND_INTEGRITYSETUP, + }, + { + .option = {"batch-mode", 0, NULL, 'q'}, + .desc = "Suppresses integritysetup confirmation questions. " + "This option is passed to the generated integritysetup " + "command(s)", + .command = COMMAND_INTEGRITYSETUP, + }, + { + .option = {"open", 0, NULL, OPT_CRYPTSETUP_OPEN}, + .desc = "Generates 'integritysetup open' commands. If this " + "option is not specified, 'integritysetup format' " + "commands are generated", + .command = COMMAND_INTEGRITYSETUP, + .flags = UTIL_OPT_FLAG_NOSHORT, + }, + /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", @@ -1465,6 +1523,8 @@ static int command_rename(void); static int command_copy(void); static int command_crypttab(void); static int command_cryptsetup(void); +static int command_integritytab(void); +static int command_integritysetup(void); static int command_convert(void); static int command_kms_plugins(void); static int command_kms_bind(void); @@ -1762,6 +1822,26 @@ static struct zkey_command zkey_commands[] = { .has_options = 1, .need_keystore = 1, }, + { + .command = COMMAND_INTEGRITYTAB, + .abbrev_len = 10, + .function = command_integritytab, + .short_desc = "Generate integritytab entries", + .long_desc = "Generate integritytab entries for selected " + "volumes", + .has_options = 1, + .need_keystore = 1, + }, + { + .command = COMMAND_INTEGRITYSETUP, + .abbrev_len = 10, + .function = command_integritysetup, + .short_desc = "Generate or run integritysetup commands", + .long_desc = "Generate or run integritysetup commands for " + "selected volumes", + .has_options = 1, + .need_keystore = 1, + }, { .command = COMMAND_CONVERT, .abbrev_len = 3, @@ -2752,6 +2832,35 @@ static int command_cryptsetup(void) return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } +/* + * Command handler for 'integritytab'. + * + * Generates integritytab entries for selected volumes + */ +static int command_integritytab(void) +{ + int rc; + + rc = keystore_integritytab(g.keystore, g.volumes); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} + +/* + * Command handler for 'integritysetup'. + * + * Generates and runs integritysetup commands for selected volumes + */ +static int command_integritysetup(void) +{ + int rc; + + rc = keystore_integritysetup(g.keystore, g.volumes, g.run, + g.batch_mode, g.open); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} + /* * Command handler for 'convert'. *