/* * zkey - Generate, re-encipher, and validate secure keys * * Copyright IBM Corp. 2017, 2018 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/util_base.h" #include "lib/util_libc.h" #include "lib/util_opt.h" #include "lib/util_panic.h" #include "lib/util_prg.h" #include "lib/zt_common.h" #include "keystore.h" #include "misc.h" #include "pkey.h" /* * Program configuration */ const struct util_prg prg = { .desc = "Manage secure AES keys", .command_args = "COMMAND [SECURE-KEY-FILE]", .args = "", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2017, .pub_last = 2018, }, UTIL_PRG_COPYRIGHT_END } }; /* * Global variables for program options */ static struct zkey_globals { char *pos_arg; char *clearkeyfile; char *outputfile; bool xts; bool verbose; long int keybits; bool tonew; bool fromold; bool complete; bool inplace; bool staged; char *name; char *description; char *volumes; char *apqns; bool noapqncheck; long int sector_size; char *volume_type; char *newname; bool run; bool batch_mode; char *keyfile; long long keyfile_offset; long long keyfile_size; long long tries; bool force; bool open; bool format; void *lib_csulcca; t_CSNBKTC dll_CSNBKTC; int pkey_fd; struct keystore *keystore; } g = { .pkey_fd = -1, .sector_size = -1, }; /* * Available commands */ #define COMMAND_GENERATE "generate" #define COMMAND_REENCIPHER "reencipher" #define COMMAND_VALIDATE "validate" #define COMMAND_IMPORT "import" #define COMMAND_EXPORT "export" #define COMMAND_LIST "list " #define COMMAND_REMOVE "remove" #define COMMAND_CHANGE "change" #define COMMAND_RENAME "rename" #define COMMAND_COPY "copy " #define COMMAND_CRYPTTAB "crypttab" #define COMMAND_CRYPTSETUP "cryptsetup" #define ZKEY_COMMAND_MAX_LEN 10 #define ENVVAR_ZKEY_REPOSITORY "ZKEY_REPOSITORY" #define DEFAULT_KEYSTORE "/etc/zkey/repository" #define OPT_CRYPTSETUP_KEYFILE 256 #define OPT_CRYPTSETUP_KEYFILE_OFFSET 257 #define OPT_CRYPTSETUP_KEYFILE_SIZE 258 #define OPT_CRYPTSETUP_TRIES 259 #define OPT_CRYPTSETUP_OPEN 260 #define OPT_CRYPTSETUP_FORMAT 261 #define OPT_NO_APQN_CHECK 262 /* * Configuration of command line options */ static struct util_opt opt_vec[] = { /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", .command = COMMAND_GENERATE, }, { .option = {"xts", 0, NULL, 'x'}, .desc = "Generate a secure AES key for the XTS cipher mode", .command = COMMAND_GENERATE, }, { .option = { "keybits", required_argument, NULL, 'k'}, .argument = "SIZE", .desc = "Size of the AES key to be generated in bits. " "Valid sizes are 128, 192, and 256 bits. Secure keys " "for use with the XTS cipher mode can only use keys " " of 128 or 256 bits. Default is 256 bits", .command = COMMAND_GENERATE, }, { .option = { "clearkey", required_argument, NULL, 'c'}, .argument = "CLEAR-KEY-FILE", .desc = "Name of a file containing the clear AES key in " "binary. If option --keybits/-k is omitted, then the " "size of the CLEAR-KEY-FILE determines the size " "of the AES key. If option --keybits/-k is specified, " "then the size of the CLEAR-KEY-FILE must match the " "specified key size. Valid file sizes are 16, 24, or " "32 bytes, and 32, or 64 bytes for keys to be used " "with XTS mode ciphers", .command = COMMAND_GENERATE, }, { .option = { "name", required_argument, NULL, 'N'}, .argument = "NAME", .desc = "Name of the secure AES key in the repository. If " "option --name/-N is specified, then the generated " "secure AES key is stored in the repository. Parameter " "SECURE-KEY-FILE is not used when option --name/-M is " "specified", .command = COMMAND_GENERATE, }, { .option = { "description", required_argument, NULL, 'd'}, .argument = "DESCRIPTION", .desc = "Textual description of the secure AES key in the " "repository", .command = COMMAND_GENERATE, }, { .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 AES key in " "the repository", .command = COMMAND_GENERATE, }, { .option = { "apqns", required_argument, NULL, 'a'}, .argument = "CARD.DOMAIN[,...]", .desc = "Comma-separated pairs of crypto cards and domains " "that are associated with the secure AES key in the " "repository", .command = COMMAND_GENERATE, }, { .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, .desc = "Do not check if the specified APQN(s) are available. " "Use this option to associate APQN(s) with a secure " "AES key that are currently not available.", .command = COMMAND_GENERATE, .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = { "sector-size", required_argument, NULL, 'S'}, .argument = "bytes", .desc = "The sector size used with dm-crypt. It must be a power " "of two and in range 512 - 4096 bytes. If this option " "is omitted, the system default sector size (512) is " "used", .command = COMMAND_GENERATE, }, #ifdef HAVE_LUKS2_SUPPORT { .option = { "volume-type", required_argument, NULL, 't'}, .argument = "type", .desc = "The type of the associated volume(s). Possible values " "are 'plain' and 'luks2'. When this option is omitted, " "the default is 'luks2'", .command = COMMAND_GENERATE, }, #endif /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", .command = COMMAND_REENCIPHER, }, { .option = { "output", required_argument, NULL, 'f'}, .argument = "OUTPUT-FILE", .desc = "Name of the output file to which the re-enciphered " "secure key is written. If this option is omitted, " "then the re-enciphered secure key will be replaced " "in the SECURE-KEY-FILE", .command = COMMAND_REENCIPHER, }, { .option = {"to-new", 0, NULL, 'n'}, .desc = "Re-enciphers a secure AES key that is currently " "enciphered with the master key in the CURRENT " "register with the master key in the NEW register", .command = COMMAND_REENCIPHER, }, { .option = {"from-old", 0, NULL, 'o'}, .desc = "Re-enciphers a secure AES key that is currently " "enciphered with the master key in the OLD register " "with the master key in the CURRENT register", .command = COMMAND_REENCIPHER, }, { .option = {"complete", 0, NULL, 'p'}, .desc = "Completes a staged re-enciphering. Use this option " "after the new CCA master key has been set (made " "active)", .command = COMMAND_REENCIPHER, }, { .option = {"in-place", 0, NULL, 'i'}, .desc = "Forces an in-place re-enchipering of a secure AES " "key. Re-enciphering from OLD to CURRENT is performed " "in-place per default", .command = COMMAND_REENCIPHER, }, { .option = {"staged", 0, NULL, 's'}, .desc = "Forces that the re-enciphering of a secure AES key is " "performed in staged mode. Re-enciphering from CURRENT " "to NEW is performed in staged mode per default", .command = COMMAND_REENCIPHER, }, { .option = { "name", required_argument, NULL, 'N'}, .argument = "NAME", .desc = "Name of the secure AES keys in the repository that " "are to be re-enciphered. You can use wild-cards to " "select the keys to re-encipher.", .command = COMMAND_REENCIPHER, }, { .option = { "apqns", required_argument, NULL, 'a'}, .argument = "CARD.DOMAIN[,...]", .desc = "Comma-separated pairs of crypto cards and domains " "that are associated with the secure AES key in the " "repository. Use this option to re-encipher all keys " "associated with specific crypto cards", .command = COMMAND_REENCIPHER, }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", .command = COMMAND_VALIDATE, }, { .option = { "name", required_argument, NULL, 'N'}, .argument = "NAME", .desc = "Name of the secure AES keys in the repository that " "are to be validated. You can use wild-cards to select " "the keys to validate.", .command = COMMAND_VALIDATE, }, { .option = { "apqns", required_argument, NULL, 'a'}, .argument = "CARD.DOMAIN[,...]", .desc = "Comma-separated pairs of crypto cards and domains " "that are associated with the secure AES key in the " "repository. Use this option to validate all keys " "associated with specific crypto cards", .command = COMMAND_VALIDATE, }, { .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, .desc = "Do not check if the associated APQN(s) are available", .command = COMMAND_VALIDATE, .flags = UTIL_OPT_FLAG_NOSHORT, }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", .command = COMMAND_IMPORT, }, { .option = { "name", required_argument, NULL, 'N'}, .argument = "NAME", .desc = "Name of the imported secure AES key in the repository", .command = COMMAND_IMPORT, }, { .option = { "description", required_argument, NULL, 'd'}, .argument = "DESCRIPTION", .desc = "Textual description of the secure AES key in the " "repository", .command = COMMAND_IMPORT, }, { .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 AES key in " "the repository", .command = COMMAND_IMPORT, }, { .option = { "apqns", required_argument, NULL, 'a'}, .argument = "CARD.DOMAIN[,...]", .desc = "Comma-separated pairs of crypto cards and domains " "that are associated with the secure AES key in the " "repository", .command = COMMAND_IMPORT, }, { .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, .desc = "Do not check if the specified APQN(s) are available. " "Use this option to associate APQN(s) with a secure " "AES key that are currently not available.", .command = COMMAND_IMPORT, .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = { "sector-size", required_argument, NULL, 'S'}, .argument = "512|4096", .desc = "The sector size used with dm-crypt. It must be power " "of two and in range 512 - 4096 bytes. If this option " "is omitted, the system default sector size (512) is " "used", .command = COMMAND_IMPORT, }, #ifdef HAVE_LUKS2_SUPPORT { .option = { "volume-type", required_argument, NULL, 't'}, .argument = "type", .desc = "The type of the associated volume(s). Possible values " "are 'plain' and 'luks2'. When this option is omitted, " "the default is 'luks2'", .command = COMMAND_IMPORT, }, #endif /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", .command = COMMAND_EXPORT, }, { .option = { "name", required_argument, NULL, 'N'}, .argument = "NAME", .desc = "Name of the secure AES key in the repository that is " "to be exported", .command = COMMAND_EXPORT, }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", .command = COMMAND_LIST, }, { .option = { "name", required_argument, NULL, 'N'}, .argument = "NAME", .desc = "Name of the secure AES keys in the repository that " "are to be listed. You can use wild-cards to select " "the keys to list.", .command = COMMAND_LIST, }, { .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 AES key in " "the repository. Use this option to list all keys " "associated with specific volumes. The device-mapper " "name (DMNAME) is optional. If specified, only those " "keys are listed where both, the volume and the device-" "mapper name matches", .command = COMMAND_LIST, }, { .option = { "apqns", required_argument, NULL, 'a'}, .argument = "CARD.DOMAIN[,...]", .desc = "Comma-separated pairs of crypto cards and domains " "that are associated with the secure AES key in the " "repository. Use this option to list all keys " "associated with specific crypto cards", .command = COMMAND_LIST, }, #ifdef HAVE_LUKS2_SUPPORT { .option = { "volume-type", required_argument, NULL, 't'}, .argument = "type", .desc = "The type of the associated volume(s). Possible values " "are 'plain' and 'luks2'. Use this option to list all " "keys with the specified volumes type.", .command = COMMAND_LIST, }, #endif /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", .command = COMMAND_REMOVE, }, { .option = { "name", required_argument, NULL, 'N'}, .argument = "NAME", .desc = "Name of the secure AES key in the repository that is " "to be removed", .command = COMMAND_REMOVE, }, { .option = {"force", 0, NULL, 'F'}, .desc = "Do not prompt for a confirmation when removing a key", .command = COMMAND_REMOVE, }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", .command = COMMAND_CHANGE, }, { .option = { "name", required_argument, NULL, 'N'}, .argument = "NAME", .desc = "Name of the secure AES key in the repository that is " "to be changed", .command = COMMAND_CHANGE, }, { .option = { "description", required_argument, NULL, 'd'}, .argument = "DESCRIPTION", .desc = "Textual description of the secure AES key in the " "repository", .command = COMMAND_CHANGE, }, { .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 AES key in " "the repository. To add pairs of volume and device-" "mapper names to the key specify '+VOLUME:DMNAME[,...]'. " "To remove pairs of volume and device-mapper names " "from the key specify '-VOLUME:DMNAME[,...]'", .command = COMMAND_CHANGE, }, { .option = { "apqns", required_argument, NULL, 'a'}, .argument = "[+|-]CARD.DOMAIN[,...]", .desc = "Comma-separated pairs of crypto cards and domains " "that are associated with the secure AES key in the " "repository. To add pairs of crypto cards and domains " "to the key specify '+CARD.DOMAIN[,...]'. To remove " "pairs of crypto cards and domains from the key " "specify '-CARD.DOMAIN[,...]'", .command = COMMAND_CHANGE, }, { .option = {"no-apqn-check", 0, NULL, OPT_NO_APQN_CHECK}, .desc = "Do not check if the specified APQN(s) are available. " "Use this option to associate APQN(s) with a secure " "AES key that are currently not available.", .command = COMMAND_CHANGE, .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = { "sector-size", required_argument, NULL, 'S'}, .argument = "0|512|4096", .desc = "The sector size used with dm-crypt. It must be power " "of two and in range 512 - 4096 bytes. Specify 0 to " "use the system default sector size (512)", .command = COMMAND_CHANGE, }, #ifdef HAVE_LUKS2_SUPPORT { .option = { "volume-type", required_argument, NULL, 't'}, .argument = "type", .desc = "The type of the associated volume(s). Possible values " "are 'plain' and 'luks2'", .command = COMMAND_CHANGE, }, #endif /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", .command = COMMAND_RENAME, }, { .option = { "name", required_argument, NULL, 'N'}, .argument = "NAME", .desc = "Name of the secure AES key in the repository that is " "to be renamed", .command = COMMAND_RENAME, }, { .option = { "new-name", required_argument, NULL, 'w'}, .argument = "NEW-NAME", .desc = "New name of the secure AES key in the repository", .command = COMMAND_RENAME, }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", .command = COMMAND_COPY, }, { .option = { "name", required_argument, NULL, 'N'}, .argument = "NAME", .desc = "Name of the secure AES key in the repository that is " "to be copied", .command = COMMAND_COPY, }, { .option = { "new-name", required_argument, NULL, 'w'}, .argument = "NEW-NAME", .desc = "New name of the secure AES key in the repository", .command = COMMAND_COPY, }, { .option = { "volumes", required_argument, NULL, 'l'}, .argument = "VOLUME:DMNAME[,...]", .desc = "Comma-separated pairs of volume and device-mapper " "names that are associated with the copied secure AES " "key in the repository. If option '--volumes/-l' is " "omitted, no volumes are associated with the copied " "key, because only one key can be associated to a " "specific volume.", .command = COMMAND_COPY, }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", .command = COMMAND_CRYPTTAB, }, { .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 AES key in " "the repository. Use this option to select the volumes " "for which a crypttab 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_CRYPTTAB, }, #ifdef HAVE_LUKS2_SUPPORT { .option = { "volume-type", required_argument, NULL, 't'}, .argument = "type", .desc = "The type of the associated volume(s). Possible values " "are 'plain' and 'luks2'. Use this option to select " "the keys by its volume type for which a crypttab " "entry is to be generated", .command = COMMAND_CRYPTTAB, }, { .option = {"key-file", required_argument, NULL, OPT_CRYPTSETUP_KEYFILE}, .argument = "FILE-NAME", .desc = "Read the passphrase from the specified file. " "The specified file is passed to the generated " "crypttab entry for LUKS2 volumes", .command = COMMAND_CRYPTTAB, .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = {"keyfile-offset", required_argument, NULL, OPT_CRYPTSETUP_KEYFILE_OFFSET}, .argument = "BYTES", .desc = "Specifies the number of bytes to skip in the file " "specified with option '--key-file'. " "The specified offset is passed to the generated " "crypttab entry for LUKS2 volumes. Not all " "distributions support the 'keyfile-offset' option in " "crypttab entries", .command = COMMAND_CRYPTTAB, .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = {"keyfile-size", required_argument, NULL, OPT_CRYPTSETUP_KEYFILE_SIZE}, .argument = "BYTES", .desc = "Specifies the number of bytes to read from the file " "specified with option '--key-file'. " "The specified size is passed to the generated " "crypttab entry for LUKS2 volumes. Not all " "distributions support the 'keyfile-size' option in " "crypttab entries", .command = COMMAND_CRYPTTAB, .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = {"tries", required_argument, NULL, OPT_CRYPTSETUP_TRIES}, .argument = "NUMBER", .desc = "Specifies how often the interactive input of the " "passphrase can be retried. " "The specified number is passed to the generated " "crypttab entry for LUKS2 volumes", .command = COMMAND_CRYPTTAB, .flags = UTIL_OPT_FLAG_NOSHORT, }, #endif /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", .command = COMMAND_CRYPTSETUP, }, { .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 AES key in " "the repository. Use this option to select the volumes " "for which a cryptsetup 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_CRYPTSETUP, }, #ifdef HAVE_LUKS2_SUPPORT { .option = { "volume-type", required_argument, NULL, 't'}, .argument = "type", .desc = "The type of the associated volume(s). Possible values " "are 'plain' and 'luks2'. Use this option to select " "the keys by its volume type for which a crypttab " "entry is to be generated", .command = COMMAND_CRYPTSETUP, }, #endif { .option = {"run", 0, NULL, 'r'}, .desc = "Runs the generated cryptsetup command", .command = COMMAND_CRYPTSETUP, }, #ifdef HAVE_LUKS2_SUPPORT { .option = {"key-file", required_argument, NULL, OPT_CRYPTSETUP_KEYFILE}, .argument = "FILE-NAME", .desc = "Read the passphrase from the specified file. " "This option is passed to the generated command(s) for " "LUKS2 volumes", .command = COMMAND_CRYPTSETUP, .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = {"keyfile-offset", required_argument, NULL, OPT_CRYPTSETUP_KEYFILE_OFFSET}, .argument = "BYTES", .desc = "Specifies the number of bytes to skip in the file " "specified with option '--key-file'. " "This option is passed to the generated command(s) for " "LUKS2 volumes", .command = COMMAND_CRYPTSETUP, .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = {"keyfile-size", required_argument, NULL, OPT_CRYPTSETUP_KEYFILE_SIZE}, .argument = "BYTES", .desc = "Specifies the number of bytes to read from the file " "specified with option '--key-file'. " "This option is passed to the generated command(s) for " "LUKS2 volumes", .command = COMMAND_CRYPTSETUP, .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = {"tries", required_argument, NULL, OPT_CRYPTSETUP_TRIES}, .argument = "NUMBER", .desc = "Specifies how often the interactive input of the " "passphrase can be retried. " "This option is passed to the generated command(s) for " "LUKS2 volumes", .command = COMMAND_CRYPTSETUP, .flags = UTIL_OPT_FLAG_NOSHORT, }, #endif { .option = {"batch-mode", 0, NULL, 'q'}, .desc = "Suppresses cryptsetup confirmation questions. " "This option is passed to the generated cryptsetup " "command(s)", .command = COMMAND_CRYPTSETUP, }, #ifdef HAVE_LUKS2_SUPPORT { .option = {"open", 0, NULL, OPT_CRYPTSETUP_OPEN}, .desc = "Generates luksOpen or plainOpen commands. For the " "plain volume type, this is the default", .command = COMMAND_CRYPTSETUP, .flags = UTIL_OPT_FLAG_NOSHORT, }, { .option = {"format", 0, NULL, OPT_CRYPTSETUP_FORMAT}, .desc = "Generates luksFormat commands. For the LUKS2 volume " "type, this is the default. If specified for the " "plain volume type, then no command is generated", .command = COMMAND_CRYPTSETUP, .flags = UTIL_OPT_FLAG_NOSHORT, }, #endif /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "COMMON OPTIONS" }, { .option = {"verbose", 0, NULL, 'V'}, .desc = "Print additional information messages during " "processing", }, UTIL_OPT_HELP, UTIL_OPT_VERSION, UTIL_OPT_END }; #define ZKEY_COMMAND_STR_LEN 80 /* * Table of supported commands */ struct zkey_command { char *command; unsigned int abbrev_len; int (*function)(void); int need_cca_library; int need_pkey_device; char *short_desc; char *long_desc; int has_options; char *pos_arg; int pos_arg_optional; char *pos_arg_alternate; char **arg_alternate_value; int need_keystore; }; static int command_generate(void); static int command_reencipher(void); static int command_validate(void); static int command_import(void); static int command_export(void); static int command_list(void); static int command_remove(void); static int command_change(void); static int command_rename(void); static int command_copy(void); static int command_crypttab(void); static int command_cryptsetup(void); static struct zkey_command zkey_commands[] = { { .command = COMMAND_GENERATE, .abbrev_len = 3, .function = command_generate, .need_pkey_device = 1, .short_desc = "Generate a secure AES key", .long_desc = "Generate a secure AES key either by " "random or from a specified clear key and store " "it either into SECURE-KEY-FILE or into the " "repository", .has_options = 1, .pos_arg = "[SECURE-KEY-FILE]", .pos_arg_optional = 1, .pos_arg_alternate = "--name/-N", .arg_alternate_value = &g.name, }, { .command = COMMAND_REENCIPHER, .abbrev_len = 2, .function = command_reencipher, .need_cca_library = 1, .need_pkey_device = 1, .short_desc = "Re-encipher an existing secure AES key", .long_desc = "Re-encipher an existing secure AES " "key that is either contained in SECURE-KEY-FILE " "or is stored in the repository with another " "CCA master key", .has_options = 1, .pos_arg = "[SECURE-KEY-FILE]", .pos_arg_optional = 1, }, { .command = COMMAND_VALIDATE, .abbrev_len = 3, .function = command_validate, .need_pkey_device = 1, .short_desc = "Validate an existing secure AES key", .long_desc = "Validate an existing secure AES key that is " "either contained in SECURE-KEY-FILE or is stored" "in the repository and print information about " "the key", .has_options = 1, .pos_arg = "[SECURE-KEY-FILE]", .pos_arg_optional = 1, }, { .command = COMMAND_IMPORT, .abbrev_len = 2, .function = command_import, .short_desc = "Import a secure AES key", .long_desc = "Import a secure AES key from a file into the " "repository", .has_options = 1, .pos_arg = "SECURE-KEY-FILE", .need_keystore = 1, }, { .command = COMMAND_EXPORT, .abbrev_len = 2, .function = command_export, .short_desc = "Export a secure AES key", .long_desc = "Export a secure AES key from the repository to " "a file", .has_options = 1, .pos_arg = "SECURE-KEY-FILE", .need_keystore = 1, }, { .command = COMMAND_LIST, .abbrev_len = 2, .function = command_list, .short_desc = "List keys in the repository", .long_desc = "List secure AES key in the repository", .has_options = 1, .need_keystore = 1, }, { .command = COMMAND_REMOVE, .abbrev_len = 3, .function = command_remove, .short_desc = "Remove a secure AES key", .long_desc = "Remove a secure AES key from the repository", .has_options = 1, .need_keystore = 1, }, { .command = COMMAND_CHANGE, .abbrev_len = 2, .function = command_change, .short_desc = "Change a secure AES key", .long_desc = "Change the properties of a secure AES key in " "the repository", .has_options = 1, .need_keystore = 1, }, { .command = COMMAND_RENAME, .abbrev_len = 3, .function = command_rename, .short_desc = "Rename a secure AES key", .long_desc = "Rename a secure AES key in the repository", .has_options = 1, .need_keystore = 1, }, { .command = COMMAND_COPY, .abbrev_len = 2, .function = command_copy, .short_desc = "Copy a secure AES key", .long_desc = "Copy a secure AES key in the repository", .has_options = 1, .need_keystore = 1, }, { .command = COMMAND_CRYPTTAB, .abbrev_len = 6, .function = command_crypttab, .short_desc = "Generate crypttab entries", .long_desc = "Generate crypttab entries for selected volumes", .has_options = 1, .need_keystore = 1, }, { .command = COMMAND_CRYPTSETUP, .abbrev_len = 6, .function = command_cryptsetup, .short_desc = "Generate or run cryptsetup commands", .long_desc = "Generate or run cryptsetup commands for " "selected volumes", .has_options = 1, .need_keystore = 1, }, { .command = NULL } }; #define pr_verbose(fmt...) if (g.verbose) \ warnx(fmt) static void print_usage_command(const struct zkey_command *command) { char command_str[ZKEY_COMMAND_STR_LEN]; unsigned int i; strncpy(command_str, command->command, sizeof(command_str) - 1); for (i = 0; i < command->abbrev_len; i++) command_str[i] = toupper(command_str[i]); printf("Usage: %s %s", program_invocation_short_name, command_str); if (command->pos_arg != NULL) printf(" %s", command->pos_arg); if (command->has_options) printf(" [OPTIONS]"); if (prg.args) printf(" %s", prg.args); printf("\n\n"); util_print_indented(command->long_desc, 0); if (command->has_options) printf("\n"); } static void print_usage_command_list(void) { struct zkey_command *cmd = zkey_commands; char command_str[ZKEY_COMMAND_STR_LEN]; unsigned int i; util_prg_print_help(); printf("COMMANDS\n"); while (cmd->command) { strcpy(command_str, cmd->command); for (i = 0; i < cmd->abbrev_len; i++) command_str[i] = toupper(command_str[i]); printf(" %-*s %s\n", ZKEY_COMMAND_MAX_LEN, command_str, cmd->short_desc); cmd++; } printf("\n"); } /* * --help printout */ static void print_help(const struct zkey_command *command) { /* Print usage */ if (!command) print_usage_command_list(); else print_usage_command(command); /* Print parameter help */ util_opt_print_help(); if (!command) { printf("\n"); printf("For more information use '%s COMMAND --help'.\n", program_invocation_short_name); } } /* * Command handler for 'generate with clear key' * * Generate a secure key from the specified clear key. */ static int command_generate_clear(void) { int rc; rc = generate_secure_key_clear(g.pkey_fd, g.pos_arg, g.keybits, g.xts, g.clearkeyfile, AUTOSELECT, AUTOSELECT, g.verbose); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'generate by random'. * * Generate a secure key by random using the pkey kernel module. */ static int command_generate_random(void) { int rc; rc = generate_secure_key_random(g.pkey_fd, g.pos_arg, g.keybits, g.xts, AUTOSELECT, AUTOSELECT, g.verbose); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'generate in repository'. * * Generate a secure key and store it in the repository. */ static int command_generate_repository(void) { int rc; if (g.sector_size < 0) g.sector_size = 0; rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes, g.apqns, g.noapqncheck, g.sector_size, g.keybits, g.xts, g.clearkeyfile, g.volume_type, g.pkey_fd); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'generate'. * * Generate a new secure key either by random or from the specified clear key. */ static int command_generate(void) { if (g.pos_arg != NULL && g.name != NULL) { warnx(" Option '--name|-N' is not valid for generating a key " "outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.apqns == NULL && g.noapqncheck) { warnx("Option '--noapqncheck' is only valid together with " "the '--apqns|-a' option"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.name != NULL) return command_generate_repository(); if (g.pos_arg != NULL) { if (g.volumes != NULL) { warnx("Option '--volumes|-l' is not valid for " "generating a key outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.apqns != NULL) { warnx("Option '--apqns|-a' is not valid for " "generating a key outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.noapqncheck) { warnx("Option '--noapqncheck' is not valid for " "generating a key outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.description != NULL) { warnx("Option '--description|-d' is not valid for " "generating a key outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } return g.clearkeyfile ? command_generate_clear() : command_generate_random(); } return EXIT_FAILURE; } /* * Command handler for 'reencipher'. * * Re-encipher the specified secure key with the NEW or CURRENT CCA master key. */ static int command_reencipher_file(void) { size_t secure_key_size; int rc, is_old_mk; u8 *secure_key; if (g.name != NULL) { warnx("Option '--name|-N' is not valid for " "re-enciphering a key outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.apqns != NULL) { warnx("Option '--apqns|-a' is not valid for " "re-enciphering a key outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.inplace) { warnx("Option '--in-place|-i' is not valid for " "re-enciphering a key outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.staged) { warnx("Option '--staged|-s' is not valid for " "re-enciphering a key outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.complete) { warnx("Option '--complete|-p' is not valid for " "re-enciphering a key outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } /* Read the secure key to be re-enciphered */ secure_key = read_secure_key(g.pos_arg, &secure_key_size, g.verbose); if (secure_key == NULL) return EXIT_FAILURE; rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, NULL, &is_old_mk, g.verbose); if (rc != 0) { warnx("The secure key in file '%s' is not valid", g.pos_arg); rc = EXIT_FAILURE; goto out; } if (!g.fromold && !g.tonew) { /* Autodetect reencipher option */ if (is_old_mk) { g.fromold = 1; util_print_indented("The secure key is currently " "enciphered with the OLD CCA " "master key and is being " "re-enciphered with the CURRENT " "CCA master key\n", 0); } else { g.tonew = 1; util_print_indented("The secure key is currently " "enciphered with the CURRENT CCA " "master key and is being " "re-enciphered with the NEW CCA " "master key\n", 0); } } /* Re-encipher the secure key */ if (g.fromold) { if (!is_old_mk) { warnx("The secure key is already enciphered " "with the CURRENT CCA master key"); rc = EXIT_FAILURE; goto out; } pr_verbose("Secure key will be re-enciphered from OLD to the " "CURRENT CCA master key"); rc = key_token_change(g.dll_CSNBKTC, secure_key, secure_key_size, METHOD_OLD_TO_CURRENT, g.verbose); if (rc != 0) { warnx("Re-encipher from OLD to CURRENT CCA " "master key has failed"); rc = EXIT_FAILURE; goto out; } } if (g.tonew) { pr_verbose("Secure key will be re-enciphered from CURRENT " "to the NEW CCA master key"); rc = key_token_change(g.dll_CSNBKTC, secure_key, secure_key_size, METHOD_CURRENT_TO_NEW, g.verbose); if (rc != 0) { warnx("Re-encipher from CURRENT to NEW CCA " "master key has failed"); rc = EXIT_FAILURE; goto out; } } pr_verbose("Secure key was re-enciphered successfully"); /* Write the migrated secure key */ rc = write_secure_key(g.outputfile ? g.outputfile : g.pos_arg, secure_key, secure_key_size, g.verbose); if (rc != 0) rc = EXIT_FAILURE; out: free(secure_key); return rc; } /* * Command handler for 'reencipher in repository'. * * Re-encipher the specified secure key with the NEW or CURRENT CCA master key. */ static int command_reencipher_repository(void) { int rc; if (g.outputfile) { warnx("Option '--output|-o' is not valid for " "re-enciphering a key in the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.inplace && g.staged) { warnx("Either '--in-place|-i' or '--staged|-s' can be " "specified, but not both"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.complete) { if (g.inplace) { warnx("Option '--in-place|-i' is not valid together " "with '--complete|-p'"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.staged) { warnx("Option '--staged|-s' is not valid together " "with '--complete|-p'"); util_prg_print_parse_error(); return EXIT_FAILURE; } } rc = keystore_reencipher_key(g.keystore, g.name, g.apqns, g.fromold, g.tonew, g.inplace, g.staged, g.complete, g.pkey_fd, g.dll_CSNBKTC); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'reencipher'. * * Re-encipher the specified secure key with the NEW or CURRENT CCA master key. */ static int command_reencipher(void) { if (g.pos_arg != NULL) return command_reencipher_file(); else return command_reencipher_repository(); return EXIT_FAILURE; } /* * Command handler for 'validate'. * * Validates the specified secure key and prints out information about it. */ static int command_validate_file(void) { char vp[VERIFICATION_PATTERN_LEN]; size_t secure_key_size; size_t clear_key_size; u8 *secure_key; int is_old_mk; int rc; if (g.name != NULL) { warnx("Option '--name|-N' is not valid for " "validating a key outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.apqns != NULL) { warnx("Option '--apqns|-a' is not valid for " "validating a key outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } if (g.noapqncheck) { warnx("Option '--noapqncheck' is not valid for " "validating a key outside of the repository"); util_prg_print_parse_error(); return EXIT_FAILURE; } /* Read the secure key to be re-enciphered */ secure_key = read_secure_key(g.pos_arg, &secure_key_size, g.verbose); if (secure_key == NULL) return EXIT_FAILURE; rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, &clear_key_size, &is_old_mk, g.verbose); if (rc != 0) { warnx("The secure key in file '%s' is not valid", g.pos_arg); rc = EXIT_FAILURE; goto out; } rc = generate_key_verification_pattern((char *)secure_key, secure_key_size, vp, sizeof(vp), g.verbose); if (rc != 0) { warnx("Failed to generate the verification pattern: %s", strerror(-rc)); warnx("Make sure that kernel module 'paes_s390' is loaded and " "that the 'paes' cipher is available"); rc = EXIT_FAILURE; goto out; } printf("Validation of secure key in file '%s':\n", g.pos_arg); printf(" Status: Valid\n"); printf(" Secure key size: %lu bytes\n", secure_key_size); printf(" Clear key size: %lu bits\n", clear_key_size); printf(" XTS type key: %s\n", secure_key_size > SECURE_KEY_SIZE ? "Yes" : "No"); printf(" Enciphered with: %s CCA master key\n", is_old_mk ? "OLD" : "CURRENT"); printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2, vp); printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2, &vp[VERIFICATION_PATTERN_LEN / 2]); out: free(secure_key); return rc; } /* * Command handler for 'validate in repository'. * * Validates the specified secure key and prints out information about it. */ static int command_validate_repository(void) { int rc; if (g.apqns == NULL && g.noapqncheck) { warnx("Option '--noapqncheck' is only valid together with " "the '--apqns|-a' option"); util_prg_print_parse_error(); return EXIT_FAILURE; } rc = keystore_validate_key(g.keystore, g.name, g.apqns, g.noapqncheck, g.pkey_fd); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'validate'. * * Validates the specified secure key and prints out information about it. */ static int command_validate(void) { if (g.pos_arg != NULL) return command_validate_file(); else return command_validate_repository(); return EXIT_FAILURE; } /* * Command handler for 'import'. * * Imports a secure key from a file into the key repository. */ static int command_import(void) { int rc; if (g.name == NULL) { misc_print_required_parm("--name/-N"); return EXIT_FAILURE; } if (g.sector_size < 0) g.sector_size = 0; if (g.apqns == NULL && g.noapqncheck) { warnx("Option '--noapqncheck' is only valid together with " "the '--apqns|-a' option"); util_prg_print_parse_error(); return EXIT_FAILURE; } rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes, g.apqns, g.noapqncheck, g.sector_size, g.pos_arg, g.volume_type); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'export'. * * Exports a secure key from the repository to a file */ static int command_export(void) { int rc; if (g.name == NULL) { misc_print_required_parm("--name/-N"); return EXIT_FAILURE; } rc = keystore_export_key(g.keystore, g.name, g.pos_arg); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'list'. * * Lists keys stored in the repository */ static int command_list(void) { int rc; rc = keystore_list_keys(g.keystore, g.name, g.volumes, g.apqns, g.volume_type); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'remove'. * * Remove a key from the repository */ static int command_remove(void) { int rc; if (g.name == NULL) { misc_print_required_parm("--name/-N"); return EXIT_FAILURE; } rc = keystore_remove_key(g.keystore, g.name, g.force); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'change'. * * Changes the properties of a key in the repository */ static int command_change(void) { int rc; if (g.name == NULL) { misc_print_required_parm("--name/-N"); return EXIT_FAILURE; } if (g.apqns == NULL && g.noapqncheck) { warnx("Option '--noapqncheck' is only valid together with " "the '--apqns|-a' option"); util_prg_print_parse_error(); return EXIT_FAILURE; } rc = keystore_change_key(g.keystore, g.name, g.description, g.volumes, g.apqns, g.noapqncheck, g.sector_size, g.volume_type); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'rname'. * * renames a key in the repository */ static int command_rename(void) { int rc; if (g.name == NULL) { misc_print_required_parm("--name/-N"); return EXIT_FAILURE; } if (g.newname == NULL) { misc_print_required_parm("--new-name/-w"); return EXIT_FAILURE; } rc = keystore_rename_key(g.keystore, g.name, g.newname); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'copy'. * * Copies a key in the repository */ static int command_copy(void) { int rc; if (g.name == NULL) { misc_print_required_parm("--name/-N"); return EXIT_FAILURE; } if (g.newname == NULL) { misc_print_required_parm("--new-name/-w"); return EXIT_FAILURE; } rc = keystore_copy_key(g.keystore, g.name, g.newname, g.volumes); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'crypttab'. * * Generates crypttab entries for selected volumes */ static int command_crypttab(void) { int rc; rc = keystore_crypttab(g.keystore, g.volumes, g.volume_type, g.keyfile, g.keyfile_offset, g.keyfile_size, g.tries); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* * Command handler for 'cryptsetup'. * * Generates and runs cryptsetup commands for selected volumes */ static int command_cryptsetup(void) { int rc; if (g.open && g.format) { warnx("Either '--open' or '--format' can be specified, but " "not both"); util_prg_print_parse_error(); return EXIT_FAILURE; } rc = keystore_cryptsetup(g.keystore, g.volumes, g.run, g.volume_type, g.keyfile, g.keyfile_offset, g.keyfile_size, g.tries, g.batch_mode, g.open, g.format); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /** * Opens the keystore. The keystore directory is either the * default directory or as specified in an environment variable */ static int open_keystore(void) { char *directory; directory = getenv(ENVVAR_ZKEY_REPOSITORY); if (directory == NULL) directory = DEFAULT_KEYSTORE; g.keystore = keystore_new(directory, g.verbose); return g.keystore == NULL ? EXIT_FAILURE : EXIT_SUCCESS; } static bool is_command(struct zkey_command *command, const char *str) { char command_str[ZKEY_COMMAND_STR_LEN]; size_t str_len = strlen(str); util_assert(sizeof(command_str) > strlen(command->command), "Buffer 'command_str' too small for %s", command->command); if (str_len < command->abbrev_len) return false; if (str_len > strlen(command->command)) return false; strncpy(command_str, command->command, str_len); if (strncasecmp(str, command_str, str_len) != 0) return false; return true; } /* * Find the command in the command table */ struct zkey_command *find_command(const char *command) { struct zkey_command *cmd = zkey_commands; while (cmd->command) { if (is_command(cmd, command)) return cmd; cmd++; } return NULL; } /* * Check if positional arguments are specified as needed by the command */ static int check_positional_arg(struct zkey_command *command) { if (command->pos_arg_optional) { if (g.pos_arg == NULL && command->arg_alternate_value != NULL && *command->arg_alternate_value == NULL) { misc_print_required_parms(command->pos_arg, command->pos_arg_alternate); return EXIT_FAILURE; } } else { if (g.pos_arg == NULL) { misc_print_required_parm(command->pos_arg); return EXIT_FAILURE; } } return EXIT_SUCCESS; } /* * Entry point */ int main(int argc, char *argv[]) { struct zkey_command *command = NULL; int arg_count = argc; char **args = argv; char *endp; int rc, c; util_prg_init(&prg); util_opt_init(opt_vec, NULL); /* Get command if one is specified */ if (argc >= 2 && strncmp(argv[1], "-", 1) != 0) { command = find_command(argv[1]); if (command == NULL) { misc_print_invalid_command(argv[1]); return EXIT_FAILURE; } arg_count = argc - 1; args = &argv[1]; if (argc >= 3 && strncmp(argv[2], "-", 1) != 0) { g.pos_arg = argv[2]; arg_count = argc - 2; args = &argv[2]; } } util_opt_set_command(command ? command->command : NULL); util_prg_set_command(command ? command->command : NULL); while (1) { c = util_opt_getopt_long(arg_count, args); if (c == -1) break; switch (c) { case 'x': g.xts = 1; break; case 'k': g.keybits = strtol(optarg, &endp, 0); if (*optarg == '\0' || *endp != '\0' || g.keybits <= 0 || (g.keybits == LONG_MAX && errno == ERANGE)) { warnx("Invalid value for '--keybits'|'-c': " "'%s'", optarg); util_prg_print_parse_error(); return EXIT_FAILURE; } break; case 'c': g.clearkeyfile = optarg; break; case 'f': g.outputfile = optarg; break; case 'n': g.tonew = 1; break; case 'o': g.fromold = 1; break; case 'p': g.complete = 1; break; case 'i': g.inplace = 1; break; case 's': g.staged = 1; break; case 'N': g.name = optarg; break; case 'd': g.description = optarg; break; case 'l': g.volumes = optarg; break; case 'a': g.apqns = optarg; break; case OPT_NO_APQN_CHECK: g.noapqncheck = 1; break; case 'S': g.sector_size = strtol(optarg, &endp, 0); if (*optarg == '\0' || *endp != '\0' || g.sector_size < 0 || (g.sector_size == LONG_MAX && errno == ERANGE)) { warnx("Invalid value for '--sector-size'|'-S': " "'%s'", optarg); util_prg_print_parse_error(); return EXIT_FAILURE; } break; #ifdef HAVE_LUKS2_SUPPORT case 't': g.volume_type = optarg; break; #endif case 'w': g.newname = optarg; break; case 'r': g.run = 1; break; case 'F': g.force = 1; break; case 'V': g.verbose = 1; break; #ifdef HAVE_LUKS2_SUPPORT case OPT_CRYPTSETUP_KEYFILE: g.keyfile = optarg; break; case OPT_CRYPTSETUP_KEYFILE_OFFSET: g.keyfile_offset = strtoll(optarg, &endp, 0); if (*optarg == '\0' || *endp != '\0' || g.keyfile_offset < 0 || (g.keyfile_offset == LLONG_MAX && errno == ERANGE)) { warnx("Invalid value for '--keyfile-offset': " "'%s'", optarg); util_prg_print_parse_error(); return EXIT_FAILURE; } break; case OPT_CRYPTSETUP_KEYFILE_SIZE: g.keyfile_size = strtoll(optarg, &endp, 0); if (*optarg == '\0' || *endp != '\0' || g.keyfile_size <= 0 || (g.keyfile_size == LLONG_MAX && errno == ERANGE)) { warnx("Invalid value for '--keyfile-size': " "'%s'", optarg); util_prg_print_parse_error(); return EXIT_FAILURE; } break; case OPT_CRYPTSETUP_TRIES: g.tries = strtoll(optarg, &endp, 0); if (*optarg == '\0' || *endp != '\0' || g.tries <= 0 || (g.tries == LLONG_MAX && errno == ERANGE)) { warnx("Invalid value for '--tries': '%s'", optarg); util_prg_print_parse_error(); return EXIT_FAILURE; } break; #endif case 'q': g.batch_mode = 1; break; #ifdef HAVE_LUKS2_SUPPORT case OPT_CRYPTSETUP_OPEN: g.open = 1; break; case OPT_CRYPTSETUP_FORMAT: g.format = 1; break; #endif case 'h': print_help(command); return EXIT_SUCCESS; case 'v': util_prg_print_version(); return EXIT_SUCCESS; default: util_opt_print_parse_error(c, args); return EXIT_FAILURE; } } if (optind < arg_count) { util_prg_print_arg_error(args[optind]); return EXIT_FAILURE; } if (command == NULL) { misc_print_missing_command(); return EXIT_FAILURE; } if (command->pos_arg != NULL) { if (check_positional_arg(command) != EXIT_SUCCESS) return EXIT_FAILURE; } if (command->need_keystore || g.pos_arg == NULL) { rc = open_keystore(); if (rc != EXIT_SUCCESS) goto out; } if (command->need_cca_library) { rc = load_cca_library(&g.lib_csulcca, &g.dll_CSNBKTC, g.verbose); if (rc != 0) { rc = EXIT_FAILURE; goto out; } } if (command->need_pkey_device) { g.pkey_fd = open_pkey_device(g.verbose); if (g.pkey_fd == -1) { rc = EXIT_FAILURE; goto out; } } umask(0077); rc = command->function(); out: if (g.lib_csulcca) dlclose(g.lib_csulcca); if (g.pkey_fd >= 0) close(g.pkey_fd); if (g.keystore) keystore_free(g.keystore); return rc; }