Subject: [PATCH] [FEAT SEC1800] zkey: Add keystore related commands
From: Philipp Rudo <prudo@linux.ibm.com>

Summary:     zkey: Add support of protected key crypto for dm-crypt.
Description: Support the usage of protected key crypto for dm-crypt disks in
             plain format by providing a tool to manage a key repository
             allowing to associate secure keys with disk partitions or logical
             volumes.
Upstream-ID: e2f92e4079eb3588574e3346a56472fb26128a7d
Problem-ID:  SEC1800

Upstream-Description:

             zkey: Add keystore related commands

             Add new commands to the zkey utility to store secure AES keys in
             the secure key repository provided by the keystore implementation
             introduced in the previous commit.

             Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
             Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
             Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>


Signed-off-by: Philipp Rudo <prudo@linux.ibm.com>
---
 zkey/misc.h |   19 -
 zkey/zkey.c | 1072 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 1024 insertions(+), 67 deletions(-)

--- 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 ch
 /**
  * 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
 	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
--- 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",
@@ -91,6 +139,49 @@ static struct util_opt opt_vec[] = {
 		.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",
 		.command = COMMAND_REENCIPHER,
@@ -119,12 +210,322 @@ static struct util_opt opt_vec[] = {
 		.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 st
 	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(voi
 		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");
@@ -250,40 +771,21 @@ static void print_help(const struct zkey
 }
 
 /*
- * 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
  *
  * 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 *
  *
  * 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
 	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;
@@ -411,11 +993,69 @@ out:
 }
 
 /*
+ * 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 *
 	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];
@@ -484,12 +1353,34 @@ struct zkey_command *find_command(const
 }
 
 /*
+ * 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;
 }
