diff --git a/zkey/misc.h b/zkey/misc.h index 262c99d1..d7bc623f 100644 --- a/zkey/misc.h +++ b/zkey/misc.h @@ -3,7 +3,7 @@ * * Local helper functions * - * Copyright 2017 IBM Corp. + * 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. @@ -37,7 +37,7 @@ void misc_print_invalid_command(const char *command) /** * An required parameter has not been specified * - * @param[in] option Parameter string + * @param[in] parm_name Parameter string */ void misc_print_required_parm(const char *parm_name) { @@ -45,4 +45,17 @@ void misc_print_required_parm(const char *parm_name) util_prg_print_parse_error(); } -#endif \ No newline at end of file +/** + * An required parameter has not been specified + * + * @param[in] parm_name1 Parameter string 1 + * @param[in] parm_name2 Parameter string 2 + */ +static void misc_print_required_parms(const char *parm_name1, + const char *parm_name2) +{ + warnx("Parameter '%s' or '%s' is required", parm_name1, parm_name2); + util_prg_print_parse_error(); +} + +#endif diff --git a/zkey/zkey.c b/zkey/zkey.c index 39c1d2df..bfbd999a 100644 --- a/zkey/zkey.c +++ b/zkey/zkey.c @@ -1,7 +1,7 @@ /* * zkey - Generate, re-encipher, and validate secure keys * - * Copyright 2017 IBM Corp. + * 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. @@ -27,6 +27,7 @@ #include "lib/util_prg.h" #include "lib/zt_common.h" +#include "keystore.h" #include "misc.h" #include "pkey.h" @@ -34,30 +35,77 @@ * Program configuration */ const struct util_prg prg = { - .desc = "Generate, re-encipher, and validate secure AES keys", - .command_args = "COMMAND SECURE-KEY-FILE", + .desc = "Manage secure AES keys", + .command_args = "COMMAND [SECURE-KEY-FILE]", .args = "", .copyright_vec = { { .owner = "IBM Corp.", .pub_first = 2017, - .pub_last = 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; + long int sector_size; + char *newname; + bool run; + bool force; + 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" /* * Configuration of command line options */ static struct util_opt opt_vec[] = { + /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", @@ -90,6 +138,49 @@ static struct util_opt opt_vec[] = { "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 = { "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, + }, + /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "OPTIONS", @@ -118,13 +209,323 @@ static struct util_opt opt_vec[] = { "with the master key in the CURRENT register", .command = COMMAND_REENCIPHER, }, + { + .option = {"complete", 0, NULL, 'p'}, + .desc = "Completes a pending re-enciphering of a secure AES " + "key that was re-enciphered with the master key in the " + "NEW register", + .command = COMMAND_REENCIPHER, + }, + { + .option = {"in-place", 0, NULL, 'i'}, + .desc = "Forces an in-place re-enchipering of a secure AES key", + .command = COMMAND_REENCIPHER, + }, + { + .option = {"staged", 0, NULL, 's'}, + .desc = "Forces a staged re-enchipering of a secure AES key", + .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, + }, + /***********************************************************/ + { + .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 = { "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, + }, + /***********************************************************/ + { + .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, + }, + /***********************************************************/ + { + .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 = { "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. If this option " + "is omitted, the system default sector size (512) is " + "used", + .command = COMMAND_CHANGE, + }, + /***********************************************************/ + { + .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, + }, + /***********************************************************/ + { + .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, + }, + { + .option = {"run", 0, NULL, 'r'}, + .desc = "Runs the generated cryptsetup command", + .command = COMMAND_CRYPTSETUP, + }, + /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, .desc = "COMMON OPTIONS" }, { .option = {"verbose", 0, NULL, 'V'}, - .desc = "Print additional information messages during processing", + .desc = "Print additional information messages during " + "processing", }, UTIL_OPT_HELP, UTIL_OPT_VERSION, @@ -139,17 +540,31 @@ static struct util_opt opt_vec[] = { struct zkey_command { char *command; unsigned int abbrev_len; - int (*function)(const char *keyfile); + 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(const char *keyfile); -static int command_reencipher(const char *keyfile); -static int command_validate(const char *keyfile); +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[] = { { @@ -159,8 +574,14 @@ static struct zkey_command zkey_commands[] = { .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", + "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, @@ -170,8 +591,12 @@ static struct zkey_command zkey_commands[] = { .need_pkey_device = 1, .short_desc = "Re-encipher an existing secure AES key", .long_desc = "Re-encipher an existing secure AES " - "key with another CCA master key", + "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, @@ -179,8 +604,100 @@ static struct zkey_command zkey_commands[] = { .function = command_validate, .need_pkey_device = 1, .short_desc = "Validate an existing secure AES key", - .long_desc = "Validate an existing secure AES key and print " - "information about the 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 } }; @@ -197,9 +714,12 @@ static void print_usage_command(const struct zkey_command *command) for (i = 0; i < command->abbrev_len; i++) command_str[i] = toupper(command_str[i]); - printf("Usage: %s %s SECURE-KEY-FILE", + printf("Usage: %s %s", program_invocation_short_name, command_str); - printf(" [OPTIONS]"); + 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"); @@ -222,7 +742,8 @@ static void print_usage_command_list(void) strcpy(command_str, cmd->command); for (i = 0; i < cmd->abbrev_len; i++) command_str[i] = toupper(command_str[i]); - printf(" %s\t%s\n", command_str, cmd->short_desc); + printf(" %-*s %s\n", ZKEY_COMMAND_MAX_LEN, command_str, + cmd->short_desc); cmd++; } printf("\n"); @@ -249,41 +770,22 @@ static void print_help(const struct zkey_command *command) } } -/* - * Global variables for program options - */ -static struct zkey_globals { - char *clearkeyfile; - char *outputfile; - int xts; - int verbose; - long int keybits; - int tonew; - int fromold; - void *lib_csulcca; - t_CSNBKTC dll_CSNBKTC; - int pkey_fd; -} g = { - .pkey_fd = -1, -}; - /* * Command handler for 'generate with clear key' * * Generate a secure key from the specified clear key. */ -static int command_generate_clear(const char *keyfile) +static int command_generate_clear(void) { int rc; - rc = generate_secure_key_clear(g.pkey_fd, keyfile, + rc = generate_secure_key_clear(g.pkey_fd, g.pos_arg, g.keybits, g.xts, g.clearkeyfile, AUTOSELECT, AUTOSELECT, g.verbose); - if (rc != 0) - rc = EXIT_FAILURE; - return rc; + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } /* @@ -291,29 +793,78 @@ static int command_generate_clear(const char *keyfile) * * Generate a secure key by random using the pkey kernel module. */ -static int command_generate_random(const char *keyfile) +static int command_generate_random(void) { int rc; - rc = generate_secure_key_random(g.pkey_fd, keyfile, + rc = generate_secure_key_random(g.pkey_fd, g.pos_arg, g.keybits, g.xts, AUTOSELECT, AUTOSELECT, g.verbose); - if (rc != 0) - rc = EXIT_FAILURE; - return rc; + 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.sector_size, g.keybits, g.xts, + g.clearkeyfile, 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(const char *keyfile) +static int command_generate(void) { - return g.clearkeyfile ? command_generate_clear(keyfile) - : command_generate_random(keyfile); + 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.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.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; } @@ -322,21 +873,52 @@ static int command_generate(const char *keyfile) * * Re-encipher the specified secure key with the NEW or CURRENT CCA master key. */ -static int command_reencipher(const char *keyfile) +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(keyfile, &secure_key_size, g.verbose); + 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", keyfile); + warnx("The secure key in file '%s' is not valid", g.pos_arg); rc = EXIT_FAILURE; goto out; } @@ -401,7 +983,7 @@ static int command_reencipher(const char *keyfile) pr_verbose("Secure key was re-enciphered successfully"); /* Write the migrated secure key */ - rc = write_secure_key(g.outputfile ? g.outputfile : keyfile, + rc = write_secure_key(g.outputfile ? g.outputfile : g.pos_arg, secure_key, secure_key_size, g.verbose); if (rc != 0) rc = EXIT_FAILURE; @@ -410,12 +992,70 @@ out: 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(const char *keyfile) +static int command_validate_file(void) { size_t secure_key_size; size_t clear_key_size; @@ -423,20 +1063,33 @@ static int command_validate(const char *keyfile) 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; + } + /* Read the secure key to be re-enciphered */ - secure_key = read_secure_key(keyfile, &secure_key_size, g.verbose); + 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", keyfile); + warnx("The secure key in file '%s' is not valid", g.pos_arg); rc = EXIT_FAILURE; goto out; } - printf("Validation of secure key in file '%s':\n", keyfile); + 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); @@ -450,6 +1103,222 @@ out: 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; + + rc = keystore_validate_key(g.keystore, g.name, g.apqns, 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; + + rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes, + g.apqns, g.sector_size, g.pos_arg); + + 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); + + 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; + } + + rc = keystore_change_key(g.keystore, g.name, g.description, g.volumes, + g.apqns, g.sector_size); + + 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); + + 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; + + rc = keystore_cryptsetup(g.keystore, g.volumes, g.run); + + 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]; @@ -483,13 +1352,35 @@ struct zkey_command *find_command(const char *command) 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; - char *keyfile = NULL; int arg_count = argc; char **args = argv; char *endp; @@ -498,7 +1389,7 @@ int main(int argc, char *argv[]) util_prg_init(&prg); util_opt_init(opt_vec, NULL); - /* Get command if one is pspecified */ + /* Get command if one is specified */ if (argc >= 2 && strncmp(argv[1], "-", 1) != 0) { command = find_command(argv[1]); if (command == NULL) { @@ -510,7 +1401,7 @@ int main(int argc, char *argv[]) args = &argv[1]; if (argc >= 3 && strncmp(argv[2], "-", 1) != 0) { - keyfile = argv[2]; + g.pos_arg = argv[2]; arg_count = argc - 2; args = &argv[2]; } @@ -550,6 +1441,47 @@ int main(int argc, char *argv[]) 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 '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; + case 'w': + g.newname = optarg; + break; + case 'r': + g.run = 1; + break; + case 'F': + g.force = 1; + break; case 'V': g.verbose = 1; break; @@ -575,31 +1507,43 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - if (keyfile == NULL) { - misc_print_required_parm("SECURE-KEY-FILE"); - 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) + 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) + if (g.pkey_fd == -1) { + rc = EXIT_FAILURE; goto out; + } } umask(0077); - rc = command->function(keyfile); + 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; }