diff --git a/s390-tools-rpmlintrc b/s390-tools-rpmlintrc index 1371e04..d7d8c9a 100644 --- a/s390-tools-rpmlintrc +++ b/s390-tools-rpmlintrc @@ -1,3 +1,4 @@ addFilter("statically-linked-binary /usr/lib/s390-tools/.*") addFilter("statically-linked-binary /usr/bin/read_values") addFilter("systemd-service-without-service_.* *@.service") +addFilter("position-independent-executable-suggested ") diff --git a/s390-tools-sles15sp1-0001-zkey-Add-properties-file-handling-routines.patch b/s390-tools-sles15sp1-0001-zkey-Add-properties-file-handling-routines.patch new file mode 100644 index 0000000..52110bc --- /dev/null +++ b/s390-tools-sles15sp1-0001-zkey-Add-properties-file-handling-routines.patch @@ -0,0 +1,506 @@ +Subject: zkey: Add properties file handling routines +From: Philipp Rudo + +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: 340da73bb7f06a9fc2aecfe4e33f1f3a17b3568d +Problem-ID: SEC1800 + +Upstream-Description: + + zkey: Add properties file handling routines + + In preparation for a new feature, introduce property file + handling routines. A property file stores key value pairs + in a text file. Optionally a hash of all keys and values + contained in the properties file can be generated to + ensure integrity of the properties file and to detect + manual modifications. + + Signed-off-by: Ingo Franzki + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Philipp Rudo +--- + zkey/Makefile | 5 + zkey/properties.c | 409 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + zkey/properties.h | 36 ++++ + 3 files changed, 448 insertions(+), 2 deletions(-) + +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -1,15 +1,16 @@ + include ../common.mak + + CPPFLAGS += -I../include +-LDLIBS += -ldl ++LDLIBS += -ldl -lcrypto + + all: zkey + + libs = $(rootdir)/libutil/libutil.a + + zkey.o: zkey.c pkey.h misc.h ++properties.o: properties.c properties.h + +-zkey: zkey.o $(libs) ++zkey: zkey.o properties.o $(libs) + + install: all + $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) +--- /dev/null ++++ b/zkey/properties.c +@@ -0,0 +1,409 @@ ++/* ++ * zkey - Generate, re-encipher, and validate secure keys ++ * ++ * Properties file handling functions ++ * ++ * Copyright IBM Corp. 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 "lib/util_libc.h" ++#include "lib/util_list.h" ++#include "lib/util_panic.h" ++ ++#include "properties.h" ++ ++struct properties { ++ struct util_list list; ++}; ++ ++struct property { ++ struct util_list_node node; ++ char *name; ++ char *value; ++}; ++ ++#define SHA256_DIGEST_LEN 32 ++#define INTEGRITY_KEY_NAME "__hash__" ++ ++#define RESTRICTED_PROPERTY_NAME_CHARS "=\n" ++#define RESTRICTED_PROPERTY_VALUE_CHARS "\n" ++ ++static int openssl_initialized; ++ ++/** ++ * Allocate and initialize a SHA-256 context ++ * ++ * @returns a SHA context ++ */ ++static EVP_MD_CTX *sha256_init(void) ++{ ++ EVP_MD_CTX *ctx; ++ int rc; ++ ++ if (!openssl_initialized) { ++ OpenSSL_add_all_algorithms(); ++ openssl_initialized = 1; ++ } ++ ++ ctx = EVP_MD_CTX_create(); ++ util_assert(ctx != NULL, ++ "Internal error: OpenSSL MD context allocation failed"); ++ ++ rc = EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); ++ util_assert(rc == 1, "Internal error: SHA-256 digest init failed"); ++ ++ return ctx; ++} ++ ++/** ++ * Add data to the SHA-256 context ++ * ++ * @parm[in] ctx the SHA context ++ * @parm[in] data the data to be hashed ++ * @parm[in] data_len the length of the data ++ */ ++static void sha256_update(EVP_MD_CTX *ctx, ++ const char *data, unsigned int data_len) ++{ ++ int rc; ++ ++ util_assert(ctx != NULL, "Internal error: OpenSSL MD context is NULL"); ++ util_assert(data != NULL || data_len == 0, ++ "Internal error: data is NULL"); ++ ++ rc = EVP_DigestUpdate(ctx, data, data_len); ++ ++ util_assert(rc == 1, "Internal error: SHA-256 digest udpdate failed"); ++} ++ ++/** ++ * Produce the digest for the SHA-256 context and free the context ++ * ++ * @parm[in] ctx the SHA context ++ * @parm[out] digest a buffer where the digest is stored ++ * @parm[in/out] digest_len on entry, *digest_len contains the size of the ++ * digest buffer, which must be large enough to hold ++ * a SHA-256 digest (32 bytes), ++ * on exit it contains the size of the digest ++ * returned in the buffer. ++ */ ++static void sha256_final(EVP_MD_CTX *ctx, ++ unsigned char *digest, unsigned int *digest_len) ++{ ++ int rc; ++ ++ util_assert(ctx != NULL, "Internal error: OpenSSL MD context is NULL"); ++ ++ if (digest != NULL && digest_len != NULL) { ++ util_assert(*digest_len >= (unsigned int)EVP_MD_CTX_size(ctx), ++ "Internal error: digest_len is too small"); ++ ++ rc = EVP_DigestFinal_ex(ctx, digest, digest_len); ++ util_assert(rc == 1, ++ "Internal error: SHA-256 digest final failed"); ++ } ++ ++ EVP_MD_CTX_destroy(ctx); ++} ++ ++/** ++ * Allocates a new properties object ++ * ++ * @returns the properties object ++ */ ++struct properties *properties_new(void) ++{ ++ struct properties *properties; ++ ++ properties = util_zalloc(sizeof(struct properties)); ++ ++ util_list_init_offset(&properties->list, ++ offsetof(struct property, node)); ++ return properties; ++} ++ ++/** ++ * Frees a properties object with all its properties ++ * ++ * @param[in] properties the properties object ++ */ ++void properties_free(struct properties *properties) ++{ ++ struct property *property; ++ ++ util_assert(properties != NULL, "Internal error: properties is NULL"); ++ ++ while ((property = util_list_start(&properties->list)) != NULL) { ++ free(property->name); ++ free(property->value); ++ util_list_remove(&properties->list, property); ++ } ++ ++ free(properties); ++} ++ ++/** ++ * Find a property by its name in the list iof properties ++ * ++ * @param[in] properties the properties object ++ * @param[in] name the name of the property to find ++ * ++ * @returns a pointer to the proerty when it has been found, or NULL if not ++ */ ++static struct property *properties_find(struct properties *properties, ++ const char *name) ++{ ++ struct property *property; ++ ++ property = util_list_start(&properties->list); ++ while (property != NULL) { ++ if (strcmp(property->name, name) == 0) ++ return property; ++ property = util_list_next(&properties->list, property); ++ } ++ return NULL; ++} ++ ++/** ++ * Adds or updates a property ++ * ++ * @param[in] properties the properties object ++ * @param[in] name the name of the property ++ * @param[in] value the value of the property ++ * ++ * @returns 0 on success, ++ * -EINVAL if the name or value contains invalid characters ++ */ ++int properties_set(struct properties *properties, ++ const char *name, const char *value) ++{ ++ struct property *property; ++ ++ util_assert(properties != NULL, "Internal error: properties is NULL"); ++ util_assert(name != NULL, "Internal error: name is NULL"); ++ util_assert(value != NULL, "Internal error: value is NULL"); ++ ++ if (strpbrk(name, RESTRICTED_PROPERTY_NAME_CHARS) != NULL) ++ return -EINVAL; ++ if (strpbrk(value, RESTRICTED_PROPERTY_VALUE_CHARS) != NULL) ++ return -EINVAL; ++ ++ property = properties_find(properties, name); ++ if (property != NULL) { ++ free(property->value); ++ property->value = util_strdup(value); ++ } else { ++ property = util_zalloc(sizeof(struct property)); ++ property->name = util_strdup(name); ++ property->value = util_strdup(value); ++ util_list_add_tail(&properties->list, property); ++ } ++ return 0; ++} ++ ++/** ++ * Gets a property ++ * ++ * @param[in] properties the properties object ++ * @param[in] name the name of the property ++ * ++ * @returns a string containing the property value, or NULL if the property ++ * was not found. ++ * Note: The returned string must be freed via free() by the caller. ++ */ ++char *properties_get(struct properties *properties, const char *name) ++{ ++ struct property *property; ++ ++ util_assert(properties != NULL, "Internal error: properties is NULL"); ++ util_assert(name != NULL, "Internal error: name is NULL"); ++ ++ property = properties_find(properties, name); ++ if (property == NULL) ++ return NULL; ++ ++ return util_strdup(property->value); ++} ++ ++/** ++ * Removes a property ++ * ++ * @param[in] properties the properties object ++ * @param[in] name the name of the property ++ * ++ * @returns 0 on success, -ENOENT if the property was not found. ++ */ ++int properties_remove(struct properties *properties, const char *name) ++{ ++ struct property *property; ++ ++ util_assert(properties != NULL, "Internal error: properties is NULL"); ++ util_assert(name != NULL, "Internal error: name is NULL"); ++ ++ property = properties_find(properties, name); ++ if (property == NULL) ++ return -ENOENT; ++ ++ free(property->name); ++ free(property->value); ++ util_list_remove(&properties->list, property); ++ return 0; ++} ++ ++/** ++ * Saves the properties to a file ++ * ++ * @param[in] properties the properties object ++ * @param[in] filename the file name ++ * @param[in] check_integrity if TRUE, an hash of the key and values is ++ * stored as part of the file. ++ * ++ * @returns 0 on success, -EIO the file could not be created ++ */ ++int properties_save(struct properties *properties, const char *filename, ++ bool check_integrity) ++{ ++ char digest_hex[SHA256_DIGEST_LEN * 2 + 1]; ++ unsigned char digest[SHA256_DIGEST_LEN]; ++ unsigned int digest_len = sizeof(digest); ++ struct property *property; ++ EVP_MD_CTX *ctx = NULL; ++ unsigned int i; ++ FILE *fp; ++ ++ util_assert(properties != NULL, "Internal error: properties is NULL"); ++ util_assert(filename != NULL, "Internal error: filename is NULL"); ++ ++ fp = fopen(filename, "w"); ++ if (fp == NULL) ++ return -EIO; ++ ++ if (check_integrity) ++ ctx = sha256_init(); ++ ++ property = util_list_start(&properties->list); ++ while (property != NULL) { ++ fprintf(fp, "%s=%s\n", property->name, property->value); ++ ++ if (check_integrity) { ++ sha256_update(ctx, property->name, ++ strlen(property->name)); ++ sha256_update(ctx, property->value, ++ strlen(property->value)); ++ } ++ ++ property = util_list_next(&properties->list, property); ++ } ++ ++ if (check_integrity) { ++ sha256_final(ctx, digest, &digest_len); ++ util_assert(digest_len <= SHA256_DIGEST_LEN, ++ "Internal error: digest length too long"); ++ ++ for (i = 0; i < digest_len; i++) ++ sprintf(&digest_hex[i * 2], "%02x", digest[i]); ++ digest_hex[digest_len * 2] = '\0'; ++ ++ fprintf(fp, "%s=%s\n", INTEGRITY_KEY_NAME, digest_hex); ++ } ++ ++ fclose(fp); ++ return 0; ++} ++ ++/** ++ * Loads the properties from a file ++ * ++ * @param[in] properties the properties object ++ * @param[in] filename the file name ++ * @param[in] check_integrity if TRUE, an hash of the key and values is ++ * compared with the hash stored as part of the file. ++ * ++ * @returns 0 on success, -EIO the file could not be created, ++ * -EPERM in case of a syntax error or an integrity error ++ */ ++int properties_load(struct properties *properties, const char *filename, ++ bool check_integrity) ++{ ++ char digest_hex[SHA256_DIGEST_LEN * 2 + 1]; ++ unsigned char digest[SHA256_DIGEST_LEN]; ++ unsigned int digest_len = sizeof(digest); ++ char *digest_read = NULL; ++ EVP_MD_CTX *ctx = NULL; ++ char line[4096]; ++ unsigned int len, i; ++ int rc = 0; ++ char *ch; ++ FILE *fp; ++ ++ util_assert(properties != NULL, "Internal error: properties is NULL"); ++ util_assert(filename != NULL, "Internal error: filename is NULL"); ++ ++ fp = fopen(filename, "r"); ++ if (fp == NULL) ++ return -EIO; ++ ++ if (check_integrity) ++ ctx = sha256_init(); ++ ++ while (fgets(line, sizeof(line), fp) != NULL) { ++ len = strlen(line); ++ if (line[len-1] == '\n') ++ line[len-1] = '\0'; ++ ch = strchr(line, '='); ++ if (ch == NULL) { ++ rc = -EPERM; ++ goto out; ++ } ++ ++ *ch = '\0'; ++ ch++; ++ ++ if (check_integrity) { ++ if (strcmp(line, INTEGRITY_KEY_NAME) == 0) { ++ digest_read = util_strdup(ch); ++ continue; ++ } ++ ++ sha256_update(ctx, line, strlen(line)); ++ sha256_update(ctx, ch, strlen(ch)); ++ } ++ ++ properties_set(properties, line, ch); ++ } ++ ++ if (check_integrity) { ++ sha256_final(ctx, digest, &digest_len); ++ ctx = NULL; ++ util_assert(digest_len <= SHA256_DIGEST_LEN, ++ "Internal error: digest length too long"); ++ ++ for (i = 0; i < digest_len; i++) ++ sprintf(&digest_hex[i * 2], "%02x", digest[i]); ++ digest_hex[digest_len * 2] = '\0'; ++ ++ if (digest_read == NULL || ++ strcmp(digest_hex, digest_read) != 0) { ++ rc = -EPERM; ++ goto out; ++ } ++ } ++ ++out: ++ if (ctx != NULL) ++ sha256_final(ctx, NULL, NULL); ++ if (digest_read != NULL) ++ free(digest_read); ++ fclose(fp); ++ return rc; ++} +--- /dev/null ++++ b/zkey/properties.h +@@ -0,0 +1,36 @@ ++/* ++ * zkey - Generate, re-encipher, and validate secure keys ++ * ++ * Properties file handling functions ++ * ++ * Copyright IBM Corp. 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. ++ */ ++ ++#ifndef PROPFILE_H ++#define PROPFILE_H ++ ++#include ++ ++struct properties; ++ ++struct properties *properties_new(void); ++ ++void properties_free(struct properties *properties); ++ ++int properties_set(struct properties *properties, ++ const char *name, const char *value); ++ ++char *properties_get(struct properties *properties, const char *name); ++ ++int properties_remove(struct properties *properties, const char *name); ++ ++int properties_save(struct properties *properties, const char *filename, ++ bool check_integrity); ++ ++int properties_load(struct properties *properties, const char *filename, ++ bool check_integrity); ++ ++#endif diff --git a/s390-tools-sles15sp1-0002-zkey-Add-build-dependency-to-OpenSSL-libcrypto.patch b/s390-tools-sles15sp1-0002-zkey-Add-build-dependency-to-OpenSSL-libcrypto.patch new file mode 100644 index 0000000..37ef6da --- /dev/null +++ b/s390-tools-sles15sp1-0002-zkey-Add-build-dependency-to-OpenSSL-libcrypto.patch @@ -0,0 +1,89 @@ +Subject: zkey: Add build dependency to OpenSSL (libcrypto) +From: Philipp Rudo + +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: 5e24f74fdefc5fe7d315df080832f1b059485f0f +Problem-ID: SEC1800 + +Upstream-Description: + + zkey: Add build dependency to OpenSSL (libcrypto) + + The integrity support for the properties file routines use + SHA-256 to build a hash of the keys and values of a property file. + The codes uses the EVP_DigestInit_ex, EVP_DigestUpdate, and + EVP_DigestFinal from the libcrypto library (OpenSSL). + + Signed-off-by: Ingo Franzki + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Philipp Rudo +--- + README.md | 6 ++++++ + zkey/Makefile | 21 ++++++++++++++++++++- + 2 files changed, 26 insertions(+), 1 deletion(-) + +--- a/README.md ++++ b/README.md +@@ -263,6 +263,7 @@ build options: + | ncurses | `HAVE_NCURSES` | hyptop | + | pfm | `HAVE_PFM` | cpacfstats | + | net-snmp | `HAVE_SNMP` | osasnmpd | ++| openssl | `HAVE_OPENSSL` | zkey | + + This table lists additional build or install options: + +@@ -365,3 +366,8 @@ the different tools are provided: + For running znetconf these programs are required: + - modprobe (kmod) + - vmcp (s390-tools) ++ ++* zkey: ++ For building the zkey tools you need openssl version 0.9.7 or newer installed ++ (openssl-devel.rpm). Tip: you may skip the zkey build by adding ++ `HAVE_OPENSSL=0` to the make invocation. +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -1,9 +1,26 @@ + include ../common.mak + ++ifeq (${HAVE_OPENSSL},0) ++ ++all: ++ $(SKIP) HAVE_OPENSSL=0 ++ ++install: ++ $(SKIP) HAVE_OPENSSL=0 ++ ++else ++ ++check_dep: ++ $(call check_dep, \ ++ "zkey", \ ++ "openssl/evp.h", \ ++ "openssl-devel", \ ++ "HAVE_OPENSSL=0") ++ + CPPFLAGS += -I../include + LDLIBS += -ldl -lcrypto + +-all: zkey ++all: check_dep zkey + + libs = $(rootdir)/libutil/libutil.a + +@@ -18,6 +35,8 @@ install: all + $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1 + $(INSTALL) -m 644 -c zkey.1 $(DESTDIR)$(MANDIR)/man1 + ++endif ++ + clean: + rm -f *.o zkey + diff --git a/s390-tools-sles15sp1-0003-zkey-Add-helper-functions-for-comma-separated-string.patch b/s390-tools-sles15sp1-0003-zkey-Add-helper-functions-for-comma-separated-string.patch new file mode 100644 index 0000000..f4badf6 --- /dev/null +++ b/s390-tools-sles15sp1-0003-zkey-Add-helper-functions-for-comma-separated-string.patch @@ -0,0 +1,276 @@ +Subject: zkey: Add helper functions for comma separated string handling +From: Philipp Rudo + +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: a090a1ffe8bc780059ebed99f19d32a2a6a3426d +Problem-ID: SEC1800 + +Upstream-Description: + + zkey: Add helper functions for comma separated string handling + + Comma separated strings are used in property values to store + multiple values in one property. These helper functions allow to + work with such comma separated strings. + + Signed-off-by: Ingo Franzki + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Philipp Rudo +--- + zkey/properties.c | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + zkey/properties.h | 12 +++ + 2 files changed, 226 insertions(+) + +--- a/zkey/properties.c ++++ b/zkey/properties.c +@@ -38,6 +38,8 @@ struct property { + #define RESTRICTED_PROPERTY_NAME_CHARS "=\n" + #define RESTRICTED_PROPERTY_VALUE_CHARS "\n" + ++#define RESTRICTED_STR_LIST_CHARS ",\n" ++ + static int openssl_initialized; + + /** +@@ -407,3 +409,215 @@ out: + fclose(fp); + return rc; + } ++ ++/** ++ * Combines a list of strings into one comma separated string ++ * ++ * @param[in] strings zero terminated array of pointers to C-strings ++ * ++ * @returns a new string. This must be freed by the caller when no longer used. ++ * returns NULL if a string contains an invalid character. ++ */ ++char *str_list_combine(const char **strings) ++{ ++ unsigned int i, size; ++ char *str; ++ ++ util_assert(strings != NULL, "Internal error: strings is NULL"); ++ ++ for (i = 0, size = 0; strings[i] != NULL; i++) { ++ if (strpbrk(strings[i], RESTRICTED_STR_LIST_CHARS) != NULL) ++ return NULL; ++ ++ if (i > 0) ++ size += 1; ++ size += strlen(strings[i]); ++ } ++ ++ str = util_zalloc(size + 1); ++ for (i = 0, size = 0; strings[i] != NULL; i++) { ++ if (i > 0) ++ strcat(str, ","); ++ strcat(str, strings[i]); ++ } ++ ++ return str; ++} ++ ++/** ++ * Splits a comma separated string into its parts ++ * ++ * @param[in] str_list the comma separated string ++ * ++ * @returns a zero terminated array of pointers to C-strings. This array ++ * and all individual C-Strings need to be freed bay the caller when ++ * no longer used. This can be done using str_list_free_string_array(). ++ */ ++char **str_list_split(const char *str_list) ++{ ++ unsigned int i, count; ++ char **list; ++ char *copy; ++ char *tok; ++ ++ util_assert(str_list != NULL, "Internal error: str_list is NULL"); ++ ++ count = str_list_count(str_list); ++ list = util_zalloc((count + 1) * sizeof(char *)); ++ ++ copy = util_strdup(str_list); ++ tok = strtok(copy, ","); ++ i = 0; ++ while (tok != NULL) { ++ list[i] = util_strdup(tok); ++ i++; ++ tok = strtok(NULL, ","); ++ } ++ ++ free(copy); ++ return list; ++} ++ ++/** ++ * Count the number of parts a comma separated string contains ++ * ++ * param[in] str_list the comma separated string ++ * ++ * @returns the number of parts ++ */ ++unsigned int str_list_count(const char *str_list) ++{ ++ unsigned int i, count; ++ ++ util_assert(str_list != NULL, "Internal error: str_list is NULL"); ++ ++ if (strlen(str_list) == 0) ++ return 0; ++ ++ for (i = 0, count = 1; str_list[i] != '\0'; i++) ++ if (str_list[i] == ',') ++ count++; ++ return count; ++} ++ ++/** ++ * Find a string in a comma separated string ++ * ++ * @param str_list the comma separated string. ++ * @param str the string to find ++ * ++ * @returns a pointer to the string within the comma separated string, ++ * or NULL if the string was not found ++ * ++ */ ++static char *str_list_find(const char *str_list, const char *str) ++{ ++ char *before; ++ char *after; ++ char *ch; ++ ++ ch = strstr(str_list, str); ++ if (ch == NULL) ++ return NULL; ++ ++ if (ch != str_list) { ++ before = ch - 1; ++ if (*before != ',') ++ return NULL; ++ } ++ ++ after = ch + strlen(str); ++ if (*after != ',' && *after != '\0') ++ return NULL; ++ ++ return ch; ++} ++ ++/** ++ * Appends a string to a comma separated string ++ * ++ * @param str_list the comma separated string. ++ * @param str the string to add ++ * ++ * @returns a new comma separated string. This must be freed by the caller when ++ * no longer used. If the string to add is already contained in the ++ * comma separated list, it is not added and NULL is returned. ++ * If the string to be added contains a comma, NULL is returned. ++ */ ++char *str_list_add(const char *str_list, const char *str) ++{ ++ char *ret; ++ ++ util_assert(str_list != NULL, "Internal error: str_list is NULL"); ++ util_assert(str != NULL, "Internal error: str is NULL"); ++ ++ if (strpbrk(str, RESTRICTED_STR_LIST_CHARS) != NULL) ++ return NULL; ++ ++ if (str_list_find(str_list, str)) ++ return NULL; ++ ++ ret = util_zalloc(strlen(str_list) + 1 + strlen(str) + 1); ++ strcpy(ret, str_list); ++ if (strlen(str_list) > 0) ++ strcat(ret, ","); ++ strcat(ret, str); ++ ++ return ret; ++} ++ ++/** ++ * Removes a string from a comma separated string ++ * ++ * @param str_list the comma separated string. ++ * @param str the string to remove ++ * ++ * @returns a new comma separated string. This must be freed by the caller when ++ * no longer used. If the string to remove is not found in the ++ * comma separated string, NULL is returned ++ */ ++char *str_list_remove(const char *str_list, const char *str) ++{ ++ char *after; ++ char *ret; ++ char *ch; ++ ++ util_assert(str_list != NULL, "Internal error: str_list is NULL"); ++ util_assert(str != NULL, "Internal error: str is NULL"); ++ ++ ch = str_list_find(str_list, str); ++ if (ch == NULL) ++ return NULL; ++ ++ after = ch + strlen(str); ++ if (*after == ',') { ++ /* there are more parts after the one to remove */ ++ ret = util_zalloc(strlen(str_list) - strlen(str) - 1 + 1); ++ strncpy(ret, str_list, ch - str_list); ++ strcat(ret, after + 1); ++ } else if (ch == str_list) { ++ /* removing the one and only part -> empty string */ ++ ret = util_zalloc(1); ++ } else { ++ /* there are no more parts after the one to remove */ ++ ret = util_zalloc(strlen(str_list) - strlen(str) - 1 + 1); ++ strncpy(ret, str_list, ch - 1 - str_list); ++ } ++ ++ return ret; ++} ++ ++/** ++ * Frees a string array (as produced by str_list_split()) ++ * ++ * @param strings a NULL terminated array of pointers to C-Strings. ++ */ ++void str_list_free_string_array(char **strings) ++{ ++ util_assert(strings != NULL, "Internal error: strings is NULL"); ++ ++ while (*strings != NULL) { ++ free((void *)*strings); ++ strings++; ++ } ++} +--- a/zkey/properties.h ++++ b/zkey/properties.h +@@ -33,4 +33,16 @@ int properties_save(struct properties *p + int properties_load(struct properties *properties, const char *filename, + bool check_integrity); + ++char *str_list_combine(const char **strings); ++ ++char **str_list_split(const char *str_list); ++ ++unsigned int str_list_count(const char *str_list); ++ ++char *str_list_add(const char *str_list, const char *str); ++ ++char *str_list_remove(const char *str_list, const char *str); ++ ++void str_list_free_string_array(char **strings); ++ + #endif diff --git a/s390-tools-sles15sp1-0004-zkey-Externalize-secure-key-back-end-functions.patch b/s390-tools-sles15sp1-0004-zkey-Externalize-secure-key-back-end-functions.patch new file mode 100644 index 0000000..5f32e43 --- /dev/null +++ b/s390-tools-sles15sp1-0004-zkey-Externalize-secure-key-back-end-functions.patch @@ -0,0 +1,1543 @@ +Subject: zkey: Externalize secure key back-end functions +From: Philipp Rudo + +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: 5872f8a21ba222f9c32f0155ce1ac0f36c12cf39 +Problem-ID: SEC1800 + +Upstream-Description: + + zkey: Externalize secure key back-end functions + + To reduce the size of the zkey.c source file, all routines that + deal with secure keys are moved to a new source file pkey.c. + + Signed-off-by: Ingo Franzki + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Philipp Rudo +--- + zkey/Makefile | 3 + zkey/pkey.c | 748 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + zkey/pkey.h | 44 +++ + zkey/zkey.c | 581 +++------------------------------------------ + 4 files changed, 832 insertions(+), 544 deletions(-) + +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -25,9 +25,10 @@ all: check_dep zkey + libs = $(rootdir)/libutil/libutil.a + + zkey.o: zkey.c pkey.h misc.h ++pkey.o: pkey.c pkey.h + properties.o: properties.c properties.h + +-zkey: zkey.o properties.o $(libs) ++zkey: zkey.o pkey.o properties.o $(libs) + + install: all + $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) +--- /dev/null ++++ b/zkey/pkey.c +@@ -0,0 +1,748 @@ ++/* ++ * zkey - Generate, re-encipher, and validate secure keys ++ * ++ * Copyright IBM Corp. 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 "lib/util_base.h" ++#include "lib/util_libc.h" ++#include "lib/util_panic.h" ++ ++#include "pkey.h" ++ ++ ++#define pr_verbose(verbose, fmt...) do { \ ++ if (verbose) \ ++ warnx(fmt); \ ++ } while (0) ++ ++#define DOUBLE_KEYSIZE_FOR_XTS(keysize, xts) ((xts) ? 2 * (keysize) : (keysize)) ++#define HALF_KEYSIZE_FOR_XTS(keysize, xts) ((xts) ? (keysize) / 2 : (keysize)) ++ ++/* ++ * Definitions for the CCA library ++ */ ++#define CCA_LIBRARY_NAME "libcsulcca.so" ++ ++#define DEFAULT_KEYBITS 256 ++ ++/** ++ * Loads the CCA library and provides the entry point of the CSNBKTC function. ++ * ++ * @param[out] lib_csulcca on return this contains the address of the CCA ++ * library. dlclose() should be used to free this ++ * when no longer needed. ++ * @param[out] dll_CSNBKTC on return this contains the address of the ++ * CSNBKTC function. ++ * @param verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, -ELIBACC in case of library load errors ++ */ ++int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose) ++{ ++ util_assert(lib_csulcca != NULL, "Internal error: lib_csulcca is NULL"); ++ util_assert(dll_CSNBKTC != NULL, "Internal error: dll_CSNBKTC is NULL"); ++ ++ /* Load the CCA library */ ++ *lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW); ++ if (*lib_csulcca == NULL) { ++ warnx("%s\nEnsure that the IBM CCA Host Libraries and " ++ "Tools are installed properly", dlerror()); ++ return -ELIBACC; ++ } ++ ++ /* Get the Key Token Change function */ ++ *dll_CSNBKTC = (t_CSNBKTC)dlsym(*lib_csulcca, "CSNBKTC"); ++ if (*dll_CSNBKTC == NULL) { ++ warnx("%s\nEnsure that the IBM CCA Host Libraries and " ++ "Tools are installed properly", dlerror()); ++ dlclose(*lib_csulcca); ++ *lib_csulcca = NULL; ++ return -ELIBACC; ++ } ++ ++ pr_verbose(verbose, "CCA library '%s' has been loaded successfully", ++ CCA_LIBRARY_NAME); ++ return 0; ++} ++ ++/** ++ * Opens the pkey device and returns its file descriptor. ++ * ++ * @param verbose if true, verbose messages are printed ++ * ++ * @returns the file descriptor or -1 to indicate an error ++ */ ++int open_pkey_device(bool verbose) ++{ ++ int pkey_fd; ++ ++ pkey_fd = open(PKEYDEVICE, O_RDWR); ++ if (pkey_fd < 0) { ++ warnx("File '%s:' %s\nEnsure that the 'pkey' kernel module " ++ "is loaded", PKEYDEVICE, strerror(errno)); ++ return -1; ++ } ++ ++ pr_verbose(verbose, "Device '%s' has been opened successfully", ++ PKEYDEVICE); ++ return pkey_fd; ++} ++ ++/** ++ * Read a secure key file and return the allocated buffer and size. ++ * ++ * @param[in] keyfile the name of the file to read ++ * @param[out] secure_key_size on return, the size of the secure key read ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @return a buffer containing the secure key, or NULL in case of an error. ++ * The returned buffer must be freed by the caller. ++ */ ++u8 *read_secure_key(const char *keyfile, size_t *secure_key_size, ++ bool verbose) ++{ ++ size_t count, size; ++ struct stat sb; ++ char *msg; ++ FILE *fp; ++ u8 *buf; ++ ++ util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); ++ util_assert(secure_key_size != NULL, ++ "Internal error: secure_key_size is NULL"); ++ ++ if (stat(keyfile, &sb)) { ++ warnx("File '%s': %s", keyfile, strerror(errno)); ++ return NULL; ++ } ++ size = sb.st_size; ++ ++ if (size != SECURE_KEY_SIZE && size != 2*SECURE_KEY_SIZE) { ++ warnx("File '%s' has an invalid size, %lu or %lu bytes " ++ "expected", keyfile, SECURE_KEY_SIZE, ++ 2 * SECURE_KEY_SIZE); ++ return NULL; ++ } ++ ++ fp = fopen(keyfile, "r"); ++ if (fp == NULL) { ++ warnx("File '%s': %s", keyfile, strerror(errno)); ++ return NULL; ++ } ++ ++ buf = util_malloc(size); ++ count = fread(buf, 1, size, fp); ++ if (count <= 0) { ++ msg = feof(fp) ? "File is too small" : strerror(errno); ++ warnx("File '%s': %s", keyfile, msg); ++ free(buf); ++ buf = NULL; ++ goto out; ++ } ++ ++ *secure_key_size = size; ++ ++ if (verbose) { ++ pr_verbose(verbose, "%lu bytes read from file '%s'", size, ++ keyfile); ++ util_hexdump_grp(stderr, NULL, buf, 4, size, 0); ++ } ++out: ++ fclose(fp); ++ return buf; ++} ++ ++/** ++ * Write a secure key file ++ * ++ * @param[in] keyfile the name of the file to write ++ * @param[in] secure_key a buffer containing the secure key ++ * @param[in] secure_key_size the size of the secure key ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 in case of success, -EIO in case of an error ++ */ ++int write_secure_key(const char *keyfile, const u8 *secure_key, ++ size_t secure_key_size, bool verbose) ++{ ++ size_t count; ++ FILE *fp; ++ ++ util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); ++ util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); ++ util_assert(secure_key_size > 0, ++ "Internal error: secure_key_size is zero"); ++ ++ fp = fopen(keyfile, "w"); ++ if (fp == NULL) { ++ warnx("File '%s': %s", keyfile, strerror(errno)); ++ return -EIO; ++ } ++ ++ count = fwrite(secure_key, 1, secure_key_size, fp); ++ if (count <= 0) { ++ warnx("File '%s': %s", keyfile, strerror(errno)); ++ fclose(fp); ++ return -EIO; ++ } ++ ++ if (verbose) { ++ pr_verbose(verbose, "%lu bytes written to file '%s'", ++ secure_key_size, keyfile); ++ util_hexdump_grp(stderr, NULL, secure_key, 4, ++ secure_key_size, 0); ++ } ++ fclose(fp); ++ return 0; ++} ++ ++/** ++ * Read a clear key file and return the allocated buffer and size ++ * ++ * @param[in] keyfile the name of the file to read ++ * @param[in] keybits the clear key size in bits. When keybits is 0, then ++ * the file size determines the keybits. ++ * @param[in] xts if true an XTS key is to be read ++ * @param[out] clear_key_size on return, the size of the clear key read ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @return a buffer containing the clear key, or NULL in case of an error. ++ * The returned buffer must be freed by the caller. ++ */ ++static u8 *read_clear_key(const char *keyfile, size_t keybits, bool xts, ++ size_t *clear_key_size, bool verbose) ++{ ++ size_t count, size, expected_size; ++ struct stat sb; ++ char *msg; ++ FILE *fp; ++ u8 *buf; ++ ++ util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); ++ util_assert(clear_key_size != NULL, ++ "Internal error: clear_key_size is NULL"); ++ ++ if (stat(keyfile, &sb)) { ++ warnx("File '%s': %s", keyfile, strerror(errno)); ++ return NULL; ++ } ++ size = sb.st_size; ++ ++ if (keybits != 0) { ++ expected_size = DOUBLE_KEYSIZE_FOR_XTS(keybits / 8, xts); ++ if (size != expected_size) { ++ warnx("File '%s' has an invalid size, " ++ "%lu bytes expected", keyfile, expected_size); ++ return NULL; ++ } ++ } else { ++ keybits = DOUBLE_KEYSIZE_FOR_XTS(size * 8, xts); ++ } ++ ++ switch (keybits) { ++ case 128: ++ break; ++ case 192: ++ if (xts) { ++ warnx("File '%s' has an invalid size, " ++ "192 bit keys are not supported with XTS", ++ keyfile); ++ return NULL; ++ } ++ break; ++ case 256: ++ break; ++ default: ++ if (xts) ++ warnx("File '%s' has an invalid size, " ++ "32 or 64 bytes expected", keyfile); ++ else ++ warnx("File '%s' has an invalid size, 16, 24 " ++ "or 32 bytes expected", keyfile); ++ return NULL; ++ } ++ ++ fp = fopen(keyfile, "r"); ++ if (fp == NULL) { ++ warnx("File '%s': %s", keyfile, strerror(errno)); ++ return NULL; ++ } ++ ++ buf = util_malloc(size); ++ count = fread(buf, 1, size, fp); ++ if (count <= 0) { ++ msg = feof(fp) ? "File is too small" : strerror(errno); ++ warnx("File '%s': %s", keyfile, msg); ++ free(buf); ++ buf = NULL; ++ goto out; ++ } ++ ++ *clear_key_size = size; ++ ++ if (verbose) { ++ pr_verbose(verbose, "%lu bytes read from file '%s'", size, ++ keyfile); ++ util_hexdump_grp(stderr, NULL, buf, 4, size, 0); ++ } ++out: ++ fclose(fp); ++ return buf; ++} ++ ++/** ++ * Generate a secure key by random ++ * ++ * @param[in] pkey_fd the pkey file descriptor ++ * @param[in] keyfile the file name of the secure key to generate ++ * @param[in] keybits the cryptographic size of the key in bits ++ * @param[in] xts if true an XTS key is generated ++ * @param[in] card the card number to use (or AUTOSELECT) ++ * @param[in] domain the domain number to use (or AUTOSELECT) ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++int generate_secure_key_random(int pkey_fd, const char *keyfile, ++ size_t keybits, bool xts, u16 card, u16 domain, ++ bool verbose) ++{ ++ struct pkey_genseck gensec; ++ size_t secure_key_size; ++ u8 *secure_key; ++ int rc; ++ ++ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); ++ util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); ++ ++ if (keybits == 0) ++ keybits = DEFAULT_KEYBITS; ++ ++ secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, xts); ++ secure_key = util_malloc(secure_key_size); ++ ++ pr_verbose(verbose, "Generate key on card %02x.%04x", card, domain); ++ ++ gensec.cardnr = card; ++ gensec.domain = domain; ++ switch (keybits) { ++ case 128: ++ gensec.keytype = PKEY_KEYTYPE_AES_128; ++ break; ++ case 192: ++ if (xts) { ++ warnx("Invalid value for '--keybits'|'-c' " ++ "for XTS: '%lu'", keybits); ++ rc = -EINVAL; ++ goto out; ++ } ++ gensec.keytype = PKEY_KEYTYPE_AES_192; ++ break; ++ case 256: ++ gensec.keytype = PKEY_KEYTYPE_AES_256; ++ break; ++ default: ++ warnx("Invalid value for '--keybits'/'-c': '%lu'", keybits); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ rc = ioctl(pkey_fd, PKEY_GENSECK, &gensec); ++ if (rc < 0) { ++ rc = -errno; ++ warnx("Failed to generate a secure key: %s", strerror(errno)); ++ goto out; ++ } ++ ++ memcpy(secure_key, &gensec.seckey, SECURE_KEY_SIZE); ++ ++ if (xts) { ++ rc = ioctl(pkey_fd, PKEY_GENSECK, &gensec); ++ if (rc < 0) { ++ rc = -errno; ++ warnx("Failed to generate a secure key: %s", ++ strerror(errno)); ++ goto out; ++ } ++ ++ memcpy(secure_key + SECURE_KEY_SIZE, &gensec.seckey, ++ SECURE_KEY_SIZE); ++ } ++ ++ pr_verbose(verbose, "Successfully generated a secure key"); ++ ++ rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose); ++ ++out: ++ free(secure_key); ++ return rc; ++} ++ ++ ++/* ++ * Generate a secure key from a clear key file ++ * ++ * @param[in] pkey_fd the pkey file descriptor ++ * @param[in] keyfile the file name of the secure key to generate ++ * @param[in] keybits the cryptographic size of the key in bits. When ++ * keybits is 0, then the clear key file size ++ * determines the keybits. ++ * @param[in] xts if true an XTS key is generated ++ * @param[in] clearkeyfile the file name of the clear key to read ++ * @param[in] card the card number to use (or AUTOSELECT) ++ * @param[in] domain the domain number to use (or AUTOSELECT) ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++int generate_secure_key_clear(int pkey_fd, const char *keyfile, ++ size_t keybits, bool xts, ++ const char *clearkeyfile, ++ u16 card, u16 domain, ++ bool verbose) ++{ ++ struct pkey_clr2seck clr2sec; ++ size_t secure_key_size; ++ size_t clear_key_size; ++ u8 *secure_key; ++ u8 *clear_key; ++ int rc; ++ ++ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); ++ util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); ++ util_assert(clearkeyfile != NULL, ++ "Internal error: clearkeyfile is NULL"); ++ ++ secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, xts); ++ secure_key = util_malloc(secure_key_size); ++ ++ clear_key = read_clear_key(clearkeyfile, keybits, xts, &clear_key_size, ++ verbose); ++ if (clear_key == NULL) ++ return -EINVAL; ++ ++ pr_verbose(verbose, "Generate key on card %02x.%04x", card, domain); ++ ++ clr2sec.cardnr = card; ++ clr2sec.domain = domain; ++ switch (HALF_KEYSIZE_FOR_XTS(clear_key_size * 8, xts)) { ++ case 128: ++ clr2sec.keytype = PKEY_KEYTYPE_AES_128; ++ break; ++ case 192: ++ clr2sec.keytype = PKEY_KEYTYPE_AES_192; ++ break; ++ case 256: ++ clr2sec.keytype = PKEY_KEYTYPE_AES_256; ++ break; ++ default: ++ warnx("Invalid clear key size: '%lu' bytes", clear_key_size); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ memcpy(&clr2sec.clrkey, clear_key, ++ HALF_KEYSIZE_FOR_XTS(clear_key_size, xts)); ++ ++ rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2sec); ++ if (rc < 0) { ++ rc = -errno; ++ warnx("Failed to generate a secure key from a " ++ "clear key: %s", strerror(errno)); ++ goto out; ++ } ++ ++ memcpy(secure_key, &clr2sec.seckey, SECURE_KEY_SIZE); ++ ++ if (xts) { ++ memcpy(&clr2sec.clrkey, clear_key + clear_key_size / 2, ++ clear_key_size / 2); ++ ++ rc = ioctl(pkey_fd, PKEY_CLR2SECK, &clr2sec); ++ if (rc < 0) { ++ rc = -errno; ++ warnx("Failed to generate a secure key from " ++ "a clear key: %s", strerror(errno)); ++ goto out; ++ } ++ ++ memcpy(secure_key+SECURE_KEY_SIZE, &clr2sec.seckey, ++ SECURE_KEY_SIZE); ++ } ++ ++ pr_verbose(verbose, ++ "Successfully generated a secure key from a clear key"); ++ ++ rc = write_secure_key(keyfile, secure_key, secure_key_size, verbose); ++ ++out: ++ memset(&clr2sec, 0, sizeof(clr2sec)); ++ memset(clear_key, 0, clear_key_size); ++ free(clear_key); ++ free(secure_key); ++ return rc; ++} ++ ++/** ++ * Prints CCA return and reason code information for certain known CCA ++ * error situations. ++ * ++ * @param return_code the CCA return code ++ * @param reason_code the CCA reason code ++ */ ++static void print_CCA_error(int return_code, int reason_code) ++{ ++ switch (return_code) { ++ case 8: ++ switch (reason_code) { ++ case 48: ++ warnx("The secure key has a CCA master key " ++ "verification pattern that is not valid"); ++ break; ++ } ++ break; ++ case 12: ++ switch (reason_code) { ++ case 764: ++ warnx("The CCA master key is not loaded and " ++ "therefore a secure key cannot be enciphered"); ++ break; ++ } ++ break; ++ } ++} ++ ++/** ++ * Re-enciphers a secure key. ++ * ++ * @param[in] dll_CSNBKTC the address of the CCA CSNBKTC function ++ * @param[in] secure_key a buffer containing the secure key ++ * @param[in] secure_key_size the size of the secure key ++ * @param[in] method the re-enciphering method. METHOD_OLD_TO_CURRENT ++ * or METHOD_CURRENT_TO_NEW. ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, -EIO in case of an error ++ */ ++int key_token_change(t_CSNBKTC dll_CSNBKTC, ++ u8 *secure_key, unsigned int secure_key_size, ++ char *method, bool verbose) ++{ ++ long exit_data_len = 0, rule_array_count; ++ unsigned char rule_array[2 * 80] = { 0, }; ++ unsigned char exit_data[4] = { 0, }; ++ long return_code, reason_code; ++ ++ util_assert(dll_CSNBKTC != NULL, "Internal error: dll_CSNBKTC is NULL"); ++ util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); ++ util_assert(secure_key_size > 0, ++ "Internal error: secure_key_size is 0"); ++ util_assert(method != NULL, "Internal error: method is NULL"); ++ ++ memcpy(rule_array, method, 8); ++ memcpy(rule_array + 8, "AES ", 8); ++ rule_array_count = 2; ++ ++ dll_CSNBKTC(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ secure_key); ++ ++ pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' returned: " ++ "return_code: %ld, reason_code: %ld", method, return_code, ++ reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ ++ if (secure_key_size == 2 * SECURE_KEY_SIZE) { ++ dll_CSNBKTC(&return_code, &reason_code, ++ &exit_data_len, exit_data, ++ &rule_array_count, rule_array, ++ secure_key + SECURE_KEY_SIZE); ++ ++ pr_verbose(verbose, "CSNBKTC (Key Token Change) with '%s' " ++ "returned: return_code: %ld, reason_code: %ld", ++ method, return_code, reason_code); ++ if (return_code != 0) { ++ print_CCA_error(return_code, reason_code); ++ return -EIO; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * Validates an XTS secure key (the second part) ++ * ++ * @param[in] pkey_fd the pkey file descriptor ++ * @param[in] secure_key a buffer containing the secure key ++ * @param[in] secure_key_size the secure key size ++ * @param[in] part1_keysize the key size of the first key part ++ * @param[in] part1_attributes the attributes of the first key part ++ * @param[out] clear_key_bitsize on return , the cryptographic size of the ++ * clear key ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++static int validate_secure_xts_key(int pkey_fd, ++ u8 *secure_key, size_t secure_key_size, ++ u16 part1_keysize, u32 part1_attributes, ++ size_t *clear_key_bitsize, bool verbose) ++{ ++ struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key; ++ struct pkey_verifykey verifykey; ++ struct secaeskeytoken *token2; ++ int rc; ++ ++ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); ++ util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); ++ ++ /* XTS uses 2 secure key tokens concatenated to each other */ ++ token2 = (struct secaeskeytoken *)(secure_key + SECURE_KEY_SIZE); ++ ++ if (secure_key_size != 2 * SECURE_KEY_SIZE) { ++ pr_verbose(verbose, "Size of secure key is too small: " ++ "%lu expected %lu", secure_key_size, ++ 2 * SECURE_KEY_SIZE); ++ return -EINVAL; ++ } ++ ++ if (token->bitsize != token2->bitsize) { ++ pr_verbose(verbose, "XTS secure key contains 2 clear keys of " ++ "different sizes"); ++ return -EINVAL; ++ } ++ if (token->keysize != token2->keysize) { ++ pr_verbose(verbose, "XTS secure key contains 2 keys of " ++ "different sizes"); ++ return -EINVAL; ++ } ++ if (memcmp(&token->mkvp, &token2->mkvp, sizeof(token->mkvp)) != 0) { ++ pr_verbose(verbose, "XTS secure key contains 2 keys using " ++ "different CCA master keys"); ++ return -EINVAL; ++ } ++ ++ memcpy(&verifykey.seckey, token2, sizeof(verifykey.seckey)); ++ ++ rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey); ++ if (rc < 0) { ++ rc = -errno; ++ pr_verbose(verbose, "Failed to validate a secure key: %s", ++ strerror(-rc)); ++ return rc; ++ } ++ ++ if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) { ++ pr_verbose(verbose, "Secure key is not an AES key"); ++ return -EINVAL; ++ } ++ ++ if (verifykey.keysize != part1_keysize) { ++ pr_verbose(verbose, "XTS secure key contains 2 keys using " ++ "different key sizes"); ++ return -EINVAL; ++ } ++ ++ if (verifykey.attributes != part1_attributes) { ++ pr_verbose(verbose, "XTS secure key contains 2 keys using " ++ "different attributes"); ++ return -EINVAL; ++ } ++ ++ if (clear_key_bitsize) ++ *clear_key_bitsize += verifykey.keysize; ++ ++ return 0; ++} ++ ++/** ++ * Validates a secure key ++ * ++ * @param[in] pkey_fd the pkey file descriptor ++ * @param[in] secure_key a buffer containing the secure key ++ * @param[in] secure_key_size the secure key size ++ * @param[out] clear_key_bitsize on return , the cryptographic size of the ++ * clear key ++ * @param[out] is_old_mk in return set to 1 to indicate if the secure key ++ * is currently enciphered by the OLD CCA master key ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++int validate_secure_key(int pkey_fd, ++ u8 *secure_key, size_t secure_key_size, ++ size_t *clear_key_bitsize, int *is_old_mk, ++ bool verbose) ++{ ++ struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key; ++ struct pkey_verifykey verifykey; ++ int rc; ++ ++ util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1"); ++ util_assert(secure_key != NULL, "Internal error: secure_key is NULL"); ++ ++ if (secure_key_size < SECURE_KEY_SIZE) { ++ pr_verbose(verbose, "Size of secure key is too small: " ++ "%lu expected %lu", secure_key_size, ++ SECURE_KEY_SIZE); ++ return -EINVAL; ++ } ++ ++ memcpy(&verifykey.seckey, token, sizeof(verifykey.seckey)); ++ ++ rc = ioctl(pkey_fd, PKEY_VERIFYKEY, &verifykey); ++ if (rc < 0) { ++ rc = -errno; ++ pr_verbose(verbose, "Failed to validate a secure key: %s", ++ strerror(-rc)); ++ return rc; ++ } ++ ++ if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) { ++ pr_verbose(verbose, "Secure key is not an AES key"); ++ return -EINVAL; ++ } ++ ++ if (clear_key_bitsize) ++ *clear_key_bitsize = verifykey.keysize; ++ ++ /* XTS uses 2 secure key tokens concatenated to each other */ ++ if (secure_key_size > SECURE_KEY_SIZE) { ++ rc = validate_secure_xts_key(pkey_fd, ++ secure_key, secure_key_size, ++ verifykey.keysize, ++ verifykey.attributes, ++ clear_key_bitsize, ++ verbose); ++ if (rc != 0) ++ return rc; ++ } ++ ++ if (is_old_mk) ++ *is_old_mk = (verifykey.attributes & ++ PKEY_VERIFY_ATTR_OLD_MKVP) != 0; ++ ++ pr_verbose(verbose, "Secure key validation completed successfully"); ++ ++ return 0; ++} +--- a/zkey/pkey.h ++++ b/zkey/pkey.h +@@ -4,7 +4,7 @@ + * This header file defines the interface to the pkey kernel module. + * It defines a set of IOCTL commands with its associated structures. + * +- * 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. +@@ -82,4 +82,44 @@ struct pkey_verifykey { + + #define PKEY_VERIFYKEY _IOWR(PKEY_IOCTL_MAGIC, 0x07, struct pkey_verifykey) + +-#endif +\ No newline at end of file ++#define METHOD_OLD_TO_CURRENT "RTCMK " ++#define METHOD_CURRENT_TO_NEW "RTNMK " ++ ++typedef void (*t_CSNBKTC)(long *return_code, ++ long *reason_code, ++ long *exit_data_length, ++ unsigned char *exit_data, ++ long *rule_array_count, ++ unsigned char *rule_array, ++ unsigned char *key_identifier); ++ ++int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose); ++ ++int open_pkey_device(bool verbose); ++ ++int generate_secure_key_random(int pkey_fd, const char *keyfile, ++ size_t keybits, bool xts, u16 card, u16 domain, ++ bool verbose); ++ ++int generate_secure_key_clear(int pkey_fd, const char *keyfile, ++ size_t keybits, bool xts, ++ const char *clearkeyfile, ++ u16 card, u16 domain, ++ bool verbose); ++ ++u8 *read_secure_key(const char *keyfile, size_t *secure_key_size, ++ bool verbose); ++ ++int write_secure_key(const char *keyfile, const u8 *secure_key, ++ size_t secure_key_size, bool verbose); ++ ++int validate_secure_key(int pkey_fd, ++ u8 *secure_key, size_t secure_key_size, ++ size_t *clear_key_bitsize, int *is_old_mk, ++ bool verbose); ++ ++int key_token_change(t_CSNBKTC dll_CSNBKTC, ++ u8 *secure_key, unsigned int secure_key_size, ++ char *method, bool verbose); ++ ++#endif +--- a/zkey/zkey.c ++++ b/zkey/zkey.c +@@ -188,9 +188,6 @@ static struct zkey_command zkey_commands + #define pr_verbose(fmt...) if (g.verbose) \ + warnx(fmt) + +-#define DOUBLE_KEYSIZE_FOR_XTS(keysize, xts) (xts) ? 2 * (keysize) : (keysize) +-#define HALF_KEYSIZE_FOR_XTS(keysize, xts) (xts) ? (keysize) / 2 : (keysize) +- + static void print_usage_command(const struct zkey_command *command) + { + char command_str[ZKEY_COMMAND_STR_LEN]; +@@ -253,19 +250,6 @@ static void print_help(const struct zkey + } + + /* +- * Definitions for the CCA library +- */ +-#define CCA_LIBRARY_NAME "libcsulcca.so" +- +-typedef void (*t_CSNBKTC)(long *return_code, +- long *reason_code, +- long *exit_data_length, +- unsigned char *exit_data, +- long *rule_array_count, +- unsigned char *rule_array, +- unsigned char *key_identifier); +- +-/* + * Global variables for program options + */ + static struct zkey_globals { +@@ -283,212 +267,6 @@ static struct zkey_globals { + .pkey_fd = -1, + }; + +-#define DEFAULT_KEYBITS 256 +- +-static int load_cca_library(void) +-{ +- /* Load the CCA library */ +- g.lib_csulcca = dlopen(CCA_LIBRARY_NAME, RTLD_GLOBAL | RTLD_NOW); +- if (g.lib_csulcca == NULL) { +- warnx("%s\nEnsure that the IBM CCA Host Libraries and " +- "Tools are installed properly", dlerror()); +- return EXIT_FAILURE; +- } +- +- /* Get the Key Token Change function */ +- g.dll_CSNBKTC = (t_CSNBKTC)dlsym(g.lib_csulcca, "CSNBKTC"); +- if (g.dll_CSNBKTC == NULL) { +- warnx("%s\nEnsure that the IBM CCA Host Libraries and " +- "Tools are installed properly", dlerror()); +- dlclose(g.lib_csulcca); +- g.lib_csulcca = NULL; +- return EXIT_FAILURE; +- } +- +- pr_verbose("CCA library '%s' has been loaded successfully", +- CCA_LIBRARY_NAME); +- +- return EXIT_SUCCESS; +-} +- +-static int open_pkey_device(void) +-{ +- g.pkey_fd = open(PKEYDEVICE, O_RDWR); +- if (g.pkey_fd < 0) { +- warnx("File '%s:' %s\nEnsure that the 'pkey' kernel module " +- "is loaded", PKEYDEVICE, strerror(errno)); +- return EXIT_FAILURE; +- } +- +- pr_verbose("Device '%s' has been opened successfully", PKEYDEVICE); +- +- return EXIT_SUCCESS; +-} +- +-/* +- * Read a secure key file and return the allocated buffer and size +- */ +-static u8 *read_secure_key(const char *keyfile, size_t *secure_key_size) +-{ +- size_t count, size; +- struct stat sb; +- char *msg; +- FILE *fp; +- u8 *buf; +- +- if (stat(keyfile, &sb)) { +- warnx("File '%s': %s", keyfile, strerror(errno)); +- return NULL; +- } +- size = sb.st_size; +- +- if (size != SECURE_KEY_SIZE && size != 2*SECURE_KEY_SIZE) { +- warnx("File '%s' has an invalid size, %lu or %lu bytes " +- "expected", keyfile, SECURE_KEY_SIZE, +- 2 * SECURE_KEY_SIZE); +- return NULL; +- } +- +- fp = fopen(keyfile, "r"); +- if (fp == NULL) { +- warnx("File '%s': %s", keyfile, strerror(errno)); +- return NULL; +- } +- +- buf = util_malloc(size); +- count = fread(buf, 1, size, fp); +- if (count <= 0) { +- msg = feof(fp) ? "File is too small" : strerror(errno); +- warnx("File '%s': %s", keyfile, msg); +- free(buf); +- buf = NULL; +- goto out; +- } +- +- *secure_key_size = size; +- +- if (g.verbose) { +- pr_verbose("%lu bytes read from file '%s'", size, keyfile); +- util_hexdump_grp(stderr, NULL, buf, 4, size, 0); +- } +-out: +- fclose(fp); +- return buf; +-} +- +-/* +- * Write a secure key file +- */ +-static int write_secure_key(const char *keyfile, const u8 *secure_key, +- size_t secure_key_size) +-{ +- size_t count; +- FILE *fp; +- +- fp = fopen(keyfile, "w"); +- if (fp == NULL) { +- warnx("File '%s': %s", keyfile, strerror(errno)); +- return EXIT_FAILURE; +- } +- +- count = fwrite(secure_key, 1, secure_key_size, fp); +- if (count <= 0) { +- warnx("File '%s': %s", keyfile, strerror(errno)); +- fclose(fp); +- return EXIT_FAILURE; +- } +- +- if (g.verbose) { +- pr_verbose("%lu bytes written to file '%s'", +- secure_key_size, keyfile); +- util_hexdump_grp(stderr, NULL, secure_key, 4, +- secure_key_size, 0); +- } +- fclose(fp); +- return EXIT_SUCCESS; +-} +- +-/* +- * Read a clear key file and return the allocated buffer and size +- * +- * When keybits is 0, then the file size determines the keybits. +- */ +-static u8 *read_clear_key(const char *keyfile, size_t keybits, +- size_t *clear_key_size) +-{ +- size_t count, size, expected_size; +- struct stat sb; +- char *msg; +- FILE *fp; +- u8 *buf; +- +- if (stat(keyfile, &sb)) { +- warnx("File '%s': %s", keyfile, strerror(errno)); +- return NULL; +- } +- size = sb.st_size; +- +- if (keybits != 0) { +- expected_size = DOUBLE_KEYSIZE_FOR_XTS(keybits / 8, g.xts); +- if (size != expected_size) { +- warnx("File '%s' has an invalid size, " +- "%lu bytes expected", keyfile, expected_size); +- return NULL; +- } +- } else { +- keybits = DOUBLE_KEYSIZE_FOR_XTS(size * 8, g.xts); +- } +- +- switch (keybits) { +- case 128: +- break; +- case 192: +- if (g.xts) { +- warnx("File '%s' has an invalid size, " +- "192 bit keys are not supported with XTS", +- keyfile); +- return NULL; +- } +- break; +- case 256: +- break; +- default: +- if (g.xts) +- warnx("File '%s' has an invalid size, " +- "32 or 64 bytes expected", keyfile); +- else +- warnx("File '%s' has an invalid size, 16, 24 " +- "or 32 bytes expected", keyfile); +- return NULL; +- } +- +- fp = fopen(keyfile, "r"); +- if (fp == NULL) { +- warnx("File '%s': %s", keyfile, strerror(errno)); +- return NULL; +- } +- +- buf = util_malloc(size); +- count = fread(buf, 1, size ,fp); +- if (count <= 0) { +- msg = feof(fp) ? "File is too small" : strerror(errno); +- warnx("File '%s': %s", keyfile, msg); +- free(buf); +- buf = NULL; +- goto out; +- } +- +- *clear_key_size = size; +- +- if (g.verbose) { +- pr_verbose("%lu bytes read from file '%s'", size, keyfile); +- util_hexdump_grp(stderr, NULL, buf, 4, size, 0); +- } +-out: +- fclose(fp); +- return buf; +-} +- + /* + * Command handler for 'generate with clear key' + * +@@ -496,76 +274,15 @@ out: + */ + static int command_generate_clear(const char *keyfile) + { +- struct pkey_clr2seck clr2sec; +- size_t secure_key_size; +- size_t clear_key_size; +- u8 *secure_key; +- u8 *clear_key; + int rc; + +- secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, g.xts); +- secure_key = util_malloc(secure_key_size); +- +- clear_key = read_clear_key(g.clearkeyfile, g.keybits, &clear_key_size); +- if (clear_key == NULL) +- return EXIT_FAILURE; +- +- clr2sec.cardnr = AUTOSELECT; +- clr2sec.domain = AUTOSELECT; +- switch (HALF_KEYSIZE_FOR_XTS(clear_key_size * 8, g.xts)) { +- case 128: +- clr2sec.keytype = PKEY_KEYTYPE_AES_128; +- break; +- case 192: +- clr2sec.keytype = PKEY_KEYTYPE_AES_192; +- break; +- case 256: +- clr2sec.keytype = PKEY_KEYTYPE_AES_256; +- break; +- default: +- warnx("Invalid clear key size: '%lu' bytes", clear_key_size); +- rc = EXIT_FAILURE; +- goto out; +- } +- +- memcpy(&clr2sec.clrkey, clear_key, +- HALF_KEYSIZE_FOR_XTS(clear_key_size, g.xts)); +- +- rc = ioctl(g.pkey_fd, PKEY_CLR2SECK, &clr2sec); +- if (rc < 0) { +- warnx("Failed to generate a secure key from a " +- "clear key: %s", strerror(errno)); ++ rc = generate_secure_key_clear(g.pkey_fd, keyfile, ++ g.keybits, g.xts, ++ g.clearkeyfile, ++ AUTOSELECT, AUTOSELECT, ++ g.verbose); ++ if (rc != 0) + rc = EXIT_FAILURE; +- goto out; +- } +- +- memcpy(secure_key, &clr2sec.seckey, SECURE_KEY_SIZE); +- +- if (g.xts) { +- memcpy(&clr2sec.clrkey, clear_key + clear_key_size / 2, +- clear_key_size / 2); +- +- rc = ioctl(g.pkey_fd, PKEY_CLR2SECK, &clr2sec); +- if (rc < 0) { +- warnx("Failed to generate a secure key from " +- "a clear key: %s", strerror(errno)); +- rc = EXIT_FAILURE; +- goto out; +- } +- +- memcpy(secure_key+SECURE_KEY_SIZE, &clr2sec.seckey, +- SECURE_KEY_SIZE); +- } +- +- pr_verbose("Successfully generated a secure key from a clear key"); +- +- rc = write_secure_key(keyfile, secure_key, secure_key_size); +- +-out: +- memset(&clr2sec, 0, sizeof(clr2sec)); +- memset(clear_key, 0, clear_key_size); +- free(clear_key); +- free(secure_key); + return rc; + } + +@@ -576,69 +293,15 @@ out: + */ + static int command_generate_random(const char *keyfile) + { +- struct pkey_genseck gensec; +- size_t secure_key_size; +- u8 *secure_key; + int rc; + +- if (g.keybits == 0) +- g.keybits = DEFAULT_KEYBITS; +- +- secure_key_size = DOUBLE_KEYSIZE_FOR_XTS(SECURE_KEY_SIZE, g.xts); +- secure_key = util_malloc(secure_key_size); +- +- gensec.cardnr = AUTOSELECT; +- gensec.domain = AUTOSELECT; +- switch (g.keybits) { +- case 128: +- gensec.keytype = PKEY_KEYTYPE_AES_128; +- break; +- case 192: +- if (g.xts) { +- warnx("Invalid value for '--keybits'|'-c' " +- "for XTS: '%lu'", g.keybits); +- rc = EXIT_FAILURE; +- goto out; +- } +- gensec.keytype = PKEY_KEYTYPE_AES_192; +- break; +- case 256: +- gensec.keytype = PKEY_KEYTYPE_AES_256; +- break; +- default: +- warnx("Invalid value for '--keybits'/'-c': '%lu'", g.keybits); ++ rc = generate_secure_key_random(g.pkey_fd, keyfile, ++ g.keybits, g.xts, ++ AUTOSELECT, AUTOSELECT, ++ g.verbose); ++ if (rc != 0) + rc = EXIT_FAILURE; +- goto out; +- } +- +- rc = ioctl(g.pkey_fd, PKEY_GENSECK, &gensec); +- if (rc < 0) { +- warnx("Failed to generate a secure key: %s", strerror(errno)); +- rc = EXIT_FAILURE; +- goto out; +- } +- +- memcpy(secure_key, &gensec.seckey, SECURE_KEY_SIZE); +- +- if (g.xts) { +- rc = ioctl(g.pkey_fd, PKEY_GENSECK, &gensec); +- if (rc < 0) { +- warnx("Failed to generate a secure key: %s", +- strerror(errno)); +- rc = EXIT_FAILURE; +- goto out; +- } +- +- memcpy(secure_key + SECURE_KEY_SIZE, &gensec.seckey, +- SECURE_KEY_SIZE); +- } +- +- pr_verbose("Successfully generated a secure key"); + +- rc = write_secure_key(keyfile, secure_key, secure_key_size); +- +-out: +- free(secure_key); + return rc; + } + +@@ -653,182 +316,6 @@ static int command_generate(const char * + : command_generate_random(keyfile); + } + +-static void print_CCA_error(int return_code, int reason_code) +-{ +- switch (return_code) { +- case 8: +- switch (reason_code) { +- case 48: +- warnx("The secure key has a CCA master key " +- "verification pattern that is not valid"); +- break; +- } +- break; +- case 12: +- switch (reason_code) { +- case 764: +- warnx("The CCA master key is not loaded and " +- "therefore a secure key cannot be enciphered"); +- break; +- } +- break; +- } +-} +- +-static int key_token_change(u8 *secure_key, unsigned int secure_key_size, +- char *method) +-{ +- long exit_data_len = 0, rule_array_count; +- unsigned char rule_array[2 * 80] = { 0, }; +- unsigned char exit_data[4] = { 0, }; +- long return_code, reason_code; +- +- memcpy(rule_array, method, 8); +- memcpy(rule_array + 8, "AES ", 8); +- rule_array_count = 2; +- +- g.dll_CSNBKTC(&return_code, &reason_code, +- &exit_data_len, exit_data, +- &rule_array_count, rule_array, +- secure_key); +- +- pr_verbose("CSNBKTC (Key Token Change) with '%s' returned: " +- "return_code: %ld, reason_code: %ld", +- method, return_code, reason_code); +- if (return_code != 0) { +- print_CCA_error(return_code, reason_code); +- return EXIT_FAILURE; +- } +- +- if (secure_key_size == 2 * SECURE_KEY_SIZE) { +- g.dll_CSNBKTC(&return_code, &reason_code, +- &exit_data_len, exit_data, +- &rule_array_count, rule_array, +- secure_key + SECURE_KEY_SIZE); +- +- pr_verbose("CSNBKTC (Key Token Change) with '%s' " +- "returned: return_code: %ld, reason_code: %ld", +- method, return_code, reason_code); +- if (return_code != 0) { +- print_CCA_error(return_code, reason_code); +- return EXIT_FAILURE; +- } +- } +- return EXIT_SUCCESS; +-} +- +-static int validate_secure_xts_key(u8 *secure_key, size_t secure_key_size, +- u16 part1_keysize, u32 part1_attributes, +- size_t *clear_key_bitsize) +-{ +- struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key; +- struct pkey_verifykey verifykey; +- struct secaeskeytoken *token2; +- int rc; +- +- /* XTS uses 2 secure key tokens concatenated to each other */ +- token2 = (struct secaeskeytoken *)(secure_key + SECURE_KEY_SIZE); +- +- if (secure_key_size != 2 * SECURE_KEY_SIZE) { +- pr_verbose("Size of secure key is too small: %lu expected %lu", +- secure_key_size, 2 * SECURE_KEY_SIZE); +- return EXIT_FAILURE; +- } +- +- if (token->bitsize != token2->bitsize) { +- pr_verbose("XTS secure key contains 2 clear keys of " +- "different sizes"); +- return EXIT_FAILURE; +- } +- if (token->keysize != token2->keysize) { +- pr_verbose("XTS secure key contains 2 keys of different " +- "sizes"); +- return EXIT_FAILURE; +- } +- if (memcmp(&token->mkvp, &token2->mkvp, sizeof(token->mkvp)) != 0) { +- pr_verbose("XTS secure key contains 2 keys using different " +- "CCA master keys"); +- return EXIT_FAILURE; +- } +- +- memcpy(&verifykey.seckey, token2, sizeof(verifykey.seckey)); +- +- rc = ioctl(g.pkey_fd, PKEY_VERIFYKEY, &verifykey); +- if (rc < 0) { +- warnx("Failed to validate a secure key: %s", strerror(errno)); +- return EXIT_FAILURE; +- } +- +- if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) { +- pr_verbose("Secure key is not an AES key"); +- return EXIT_FAILURE; +- } +- +- if (verifykey.keysize != part1_keysize) { +- pr_verbose("XTS secure key contains 2 keys using different " +- "key sizes"); +- return EXIT_FAILURE; +- } +- +- if (verifykey.attributes != part1_attributes) { +- pr_verbose("XTS secure key contains 2 keys using different " +- "attributes"); +- return EXIT_FAILURE; +- } +- +- if (clear_key_bitsize) +- *clear_key_bitsize += verifykey.keysize; +- +- return EXIT_SUCCESS; +-} +- +-static int validate_secure_key(u8 *secure_key, size_t secure_key_size, +- size_t *clear_key_bitsize, int *is_old_mk) +-{ +- struct secaeskeytoken *token = (struct secaeskeytoken *)secure_key; +- struct pkey_verifykey verifykey; +- int rc; +- +- if (secure_key_size < SECURE_KEY_SIZE) { +- pr_verbose("Size of secure key is too small: %lu expected %lu", +- secure_key_size, SECURE_KEY_SIZE); +- return EXIT_FAILURE; +- } +- +- memcpy(&verifykey.seckey, token, sizeof(verifykey.seckey)); +- +- rc = ioctl(g.pkey_fd, PKEY_VERIFYKEY, &verifykey); +- if (rc < 0) { +- warnx("Failed to validate a secure key: %s", strerror(errno)); +- return EXIT_FAILURE; +- } +- +- if ((verifykey.attributes & PKEY_VERIFY_ATTR_AES) == 0) { +- pr_verbose("Secure key is not an AES key"); +- return EXIT_FAILURE; +- } +- +- if (clear_key_bitsize) +- *clear_key_bitsize = verifykey.keysize; +- +- /* XTS uses 2 secure key tokens concatenated to each other */ +- if (secure_key_size > SECURE_KEY_SIZE) { +- rc = validate_secure_xts_key(secure_key, secure_key_size, +- verifykey.keysize, +- verifykey.attributes, +- clear_key_bitsize); +- if (rc != EXIT_SUCCESS) +- return rc; +- } +- +- if (is_old_mk) +- *is_old_mk = (verifykey.attributes & +- PKEY_VERIFY_ATTR_OLD_MKVP) != 0; +- +- pr_verbose("Secure key validation completed successfully"); +- +- return EXIT_SUCCESS; +-} + + /* + * Command handler for 'reencipher'. +@@ -842,14 +329,15 @@ static int command_reencipher(const char + u8 *secure_key; + + /* Read the secure key to be re-enciphered */ +- secure_key = read_secure_key(keyfile, &secure_key_size); ++ secure_key = read_secure_key(keyfile, &secure_key_size, g.verbose); + if (secure_key == NULL) + return EXIT_FAILURE; + +- rc = validate_secure_key(secure_key, secure_key_size, NULL, +- &is_old_mk); +- if (rc != EXIT_SUCCESS) { ++ 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); ++ rc = EXIT_FAILURE; + goto out; + } + +@@ -884,10 +372,14 @@ static int command_reencipher(const char + pr_verbose("Secure key will be re-enciphered from OLD to the " + "CURRENT CCA master key"); + +- rc = key_token_change(secure_key, secure_key_size, "RTCMK "); +- if (rc != EXIT_SUCCESS) { ++ 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; + } + } +@@ -895,10 +387,13 @@ static int command_reencipher(const char + pr_verbose("Secure key will be re-enciphered from CURRENT " + "to the NEW CCA master key"); + +- rc = key_token_change(secure_key, secure_key_size, "RTNMK "); +- if (rc != EXIT_SUCCESS) { ++ 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; + } + } +@@ -907,7 +402,9 @@ static int command_reencipher(const char + + /* Write the migrated secure key */ + rc = write_secure_key(g.outputfile ? g.outputfile : keyfile, +- secure_key, secure_key_size); ++ secure_key, secure_key_size, g.verbose); ++ if (rc != 0) ++ rc = EXIT_FAILURE; + out: + free(secure_key); + return rc; +@@ -927,14 +424,15 @@ static int command_validate(const char * + int rc; + + /* Read the secure key to be re-enciphered */ +- secure_key = read_secure_key(keyfile, &secure_key_size); ++ secure_key = read_secure_key(keyfile, &secure_key_size, g.verbose); + if (secure_key == NULL) + return EXIT_FAILURE; + +- rc = validate_secure_key(secure_key, secure_key_size, &clear_key_size, +- &is_old_mk); +- if (rc != EXIT_SUCCESS) { ++ 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); ++ rc = EXIT_FAILURE; + goto out; + } + +@@ -1083,13 +581,14 @@ int main(int argc, char *argv[]) + } + + if (command->need_cca_library) { +- rc = load_cca_library(); +- if (rc != EXIT_SUCCESS) ++ rc = load_cca_library(&g.lib_csulcca, &g.dll_CSNBKTC, ++ g.verbose); ++ if (rc != 0) + goto out; + } + if (command->need_pkey_device) { +- rc = open_pkey_device(); +- if (rc != EXIT_SUCCESS) ++ g.pkey_fd = open_pkey_device(g.verbose); ++ if (g.pkey_fd == -1) + goto out; + } + diff --git a/s390-tools-sles15sp1-0005-zkey-Add-keystore-implementation.patch b/s390-tools-sles15sp1-0005-zkey-Add-keystore-implementation.patch new file mode 100644 index 0000000..7a007de --- /dev/null +++ b/s390-tools-sles15sp1-0005-zkey-Add-keystore-implementation.patch @@ -0,0 +1,3427 @@ +Subject: zkey: Add keystore implementation +From: Philipp Rudo + +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: c944f23d7e1f983499b4c5fcf04430dc49902f04 +Problem-ID: SEC1800 + +Upstream-Description: + + zkey: Add keystore implementation + + Add a keystore implementation that stores secure AES keys in a + key repository, located in a directory, e.g. '/etc/zkey/repository'. + The keystore allows you to generate, validate, re-encipher, modify, + list, delete, etc secure keys. + + Signed-off-by: Ingo Franzki + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Philipp Rudo +--- + zkey/Makefile | 3 + zkey/keystore.c | 3299 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + zkey/keystore.h | 77 + + 3 files changed, 3378 insertions(+), 1 deletion(-) + +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -27,8 +27,9 @@ libs = $(rootdir)/libutil/libutil.a + zkey.o: zkey.c pkey.h misc.h + pkey.o: pkey.c pkey.h + properties.o: properties.c properties.h ++keystore.o: keystore.c keystore.h properties.h + +-zkey: zkey.o pkey.o properties.o $(libs) ++zkey: zkey.o pkey.o properties.o keystore.o $(libs) + + install: all + $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) +--- /dev/null ++++ b/zkey/keystore.c +@@ -0,0 +1,3299 @@ ++/* ++ * zkey - Generate, re-encipher, and validate secure keys ++ * ++ * Keystore handling functions ++ * ++ * Copyright IBM Corp. 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 ++#include ++ ++#include "lib/util_base.h" ++#include "lib/util_file.h" ++#include "lib/util_libc.h" ++#include "lib/util_panic.h" ++#include "lib/util_path.h" ++#include "lib/util_rec.h" ++ ++#include "keystore.h" ++#include "pkey.h" ++#include "properties.h" ++ ++struct key_filenames { ++ char *skey_filename; ++ char *info_filename; ++ char *renc_filename; ++}; ++ ++#define FILE_EXTENSION_LEN 5 ++#define SKEY_FILE_EXTENSION ".skey" ++#define INFO_FILE_EXTENSION ".info" ++#define RENC_FILE_EXTENSION ".renc" ++ ++#define LOCK_FILE_NAME ".lock" ++ ++#define PROP_NAME_KEY_TYPE "key-type" ++#define PROP_NAME_CIPHER "cipher" ++#define PROP_NAME_IV_MODE "iv-mode" ++#define PROP_NAME_DESCRIPTION "description" ++#define PROP_NAME_VOLUMES "volumes" ++#define PROP_NAME_APQNS "apqns" ++#define PROP_NAME_SECTOR_SIZE "sector-size" ++#define PROP_NAME_CREATION_TIME "creation-time" ++#define PROP_NAME_CHANGE_TIME "update-time" ++#define PROP_NAME_REENC_TIME "reencipher-time" ++ ++#define IS_XTS(secure_key_size) (secure_key_size > SECURE_KEY_SIZE ? 1 : 0) ++ ++#define REC_KEY "Key" ++#define REC_DESCRIPTION "Description" ++#define REC_SEC_KEY_SIZE "Secure key size" ++#define REC_CLR_KEY_SIZE "Clear key size" ++#define REC_XTS "XTS type key" ++#define REC_VOLUMES "Volumes" ++#define REC_APQNS "APQNs" ++#define REC_KEY_FILE "Key file name" ++#define REC_SECTOR_SIZE "Sector size" ++#define REC_STATUS "Status" ++#define REC_MASTERKEY "Encrypted with" ++#define REC_CREATION_TIME "Created" ++#define REC_CHANGE_TIME "Changed" ++#define REC_REENC_TIME "Re-enciphered" ++ ++#define pr_verbose(keystore, fmt...) do { \ ++ if (keystore->verbose) \ ++ warnx(fmt); \ ++ } while (0) ++ ++/** ++ * Gets the file names of the .skey and .info and .renc files for a named ++ * key in the key strore's directory ++ * ++ * @param[in] keystore the key store ++ * @param[in] name the name of the key ++ * @param[out] names is filled with the names of the files ++ * ++ * @returns 0 for success or a negative errno in case of an error* ++ */ ++static int _keystore_get_key_filenames(struct keystore *keystore, ++ const char *name, ++ struct key_filenames *names) ++{ ++ if (strpbrk(name, "/\\ *?'\"")) { ++ warnx("Key name '%s' contains invalid characters", name); ++ return -EINVAL; ++ } ++ ++ util_asprintf(&names->skey_filename, "%s/%s%s", keystore->directory, ++ name, SKEY_FILE_EXTENSION); ++ util_asprintf(&names->info_filename, "%s/%s%s", keystore->directory, ++ name, INFO_FILE_EXTENSION); ++ util_asprintf(&names->renc_filename, "%s/%s%s", keystore->directory, ++ name, RENC_FILE_EXTENSION); ++ ++ pr_verbose(keystore, "File names for key '%s': '%s' and '%s'", name, ++ names->skey_filename, names->info_filename); ++ return 0; ++} ++ ++/** ++ * Checks if the .renc file exists. ++ * ++ * @param[in] file_names names of the files ++ * ++ * @returns 1 if the file exist, 0 if the file do not exist ++ */ ++static int _keystore_reencipher_key_exists(struct key_filenames *file_names) ++{ ++ struct stat sb; ++ int rc; ++ ++ rc = stat(file_names->renc_filename, &sb); ++ if (rc == 0 && !S_ISREG(sb.st_mode)) ++ rc = 1; ++ ++ return !rc; ++} ++ ++/** ++ * Checks if both, the .skey and the .info (and .renc) files exist. ++ * ++ * @param[in] file_names names of the files ++ * ++ * @returns 1 if all files exist, 0 if all files do not exist, -1 if one ++ * file exists but other one does not exist (inconsistent state) ++ */ ++static int _keystore_exists_keyfiles(struct key_filenames *file_names) ++{ ++ struct stat sb_skey, sb_info; ++ int rc_skey, rc_info; ++ ++ rc_skey = stat(file_names->skey_filename, &sb_skey); ++ if (rc_skey == 0 && !S_ISREG(sb_skey.st_mode)) ++ rc_skey = 1; ++ ++ rc_info = stat(file_names->info_filename, &sb_info); ++ if (rc_info == 0 && !S_ISREG(sb_info.st_mode)) ++ rc_info = 1; ++ ++ if (rc_skey == 0 && rc_info == 0) ++ return 1; ++ if (rc_skey != 0 && rc_info != 0 && ++ _keystore_reencipher_key_exists(file_names) == 0) ++ return 0; ++ return -1; ++} ++ ++/** ++ * Checks if the files belonging to a key exist. If not an appropriate error ++ * message is issued. ++ * ++ * @param[in] file_names names of the files ++ * @param[in] name name of the key ++ * ++ * @returns 0 if the files exist, -ENOENT if the files do not exist, -EPERM if ++ * one file exists but the other does not exist (inconsistent state) ++ */ ++static int _keystore_ensure_keyfiles_exist(struct key_filenames *file_names, ++ const char *name) ++{ ++ int rc; ++ ++ rc = _keystore_exists_keyfiles(file_names); ++ if (rc == 0) { ++ warnx("Key '%s' does not exist", name); ++ return -ENOENT; ++ } ++ if (rc == -1) { ++ warnx("Key '%s' is in an inconsistent state", name); ++ return -EPERM; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Checks if the files belonging to a key do not exist. If they files exist, ++ * an appropriate error message is issued. ++ * ++ * @param[in] file_names names of the files ++ * @param[in] name name of the key ++ * ++ * @returns 0 if the files exist, -EEXIST if the files exist already, -EPERM if ++ * one file exists but the other does not exist (inconsistent state) ++ */ ++static int _keystore_ensure_keyfiles_not_exist(struct key_filenames *file_names, ++ const char *name) ++{ ++ int rc; ++ ++ rc = _keystore_exists_keyfiles(file_names); ++ if (rc == 1) { ++ warnx("Key '%s' exists already", name); ++ return -EEXIST; ++ } ++ if (rc == -1) { ++ warnx("Key '%s' is in an inconsistent state", name); ++ return -EPERM; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Frees the file names stored inside the struct key_filenames ++ * ++ * @param[in] names names of the files ++ */ ++static void _keystore_free_key_filenames(struct key_filenames *names) ++{ ++ if (names->skey_filename) ++ free(names->skey_filename); ++ if (names->info_filename) ++ free(names->info_filename); ++ if (names->renc_filename) ++ free(names->renc_filename); ++} ++ ++/** ++ * Sets the file permissions of the file to the permissions and the group ++ * of the repository directory ++ * ++ * @param[in] keystroe the keystore ++ * @param[in] filename the name of the file to set permissions for ++ * ++ * @returns 0 on success, or a negative errno value on failure ++ */ ++static int _keystore_set_file_permission(struct keystore *keystore, ++ const char *filename) ++{ ++ int rc; ++ ++ if (chmod(filename, keystore->mode) != 0) { ++ rc = -errno; ++ warnx("chmod faild on file '%s': %s", filename, strerror(-rc)); ++ return rc; ++ } ++ ++ if (chown(filename, geteuid(), keystore->owner) != 0) { ++ rc = -errno; ++ warnx("chown faild on file '%s': %s", filename, strerror(-rc)); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++/** ++ * Checks if the sector size is power of two and in range 512 - 4096 bytes. ++ * ++ * @param[in] sector_size the sector size ++ * ++ * @returns 1 if the sector size is valid, 0 otherwise ++ */ ++static int _keystore_valid_sector_size(size_t sector_size) ++{ ++ if (sector_size == 0) ++ return 1; ++ if (sector_size < 512 || sector_size > 4096) ++ return 0; ++ if (sector_size & (sector_size - 1)) ++ return 0; ++ return 1; ++} ++ ++typedef int (*check_association_t)(const char *value, bool remove, ++ char **normalized, void *private); ++ ++/** ++ * Set an association property. For each object set function check_func is ++ * called (if not NULL). ++ * ++ * @param[in/out] key_props the properties object to modify ++ * @param[in] property the name of the property to modify ++ * @param[in] newvalue the new value(s) to add, remove or set ++ * @param[in] msg_obj the name of the object for error messages ++ * @param[in] check_func a function to call on each object before it is ++ * added, removed or set to the property ++ * @param[in] check_private a private pointer passed to check_func ++ * ++ * @returns 0 for success, or a negative errno value in case of an error, or ++ * whatever check_func returns if check_func returns a non-zero value. ++ */ ++static int _keystore_set_association(struct properties *key_props, ++ const char *property, ++ const char *newvalue, ++ const char *msg_obj, ++ check_association_t check_func, ++ void *check_private) ++{ ++ char *normalized = NULL; ++ char **newvals = NULL; ++ char *value = NULL; ++ char *changedval; ++ char *newval; ++ int i, rc = 0; ++ ++ newvals = str_list_split(newvalue); ++ if (newvals == NULL) ++ return -EINVAL; ++ ++ for (i = 0; newvals[i] != NULL; i++) { ++ if (check_func != NULL) { ++ rc = check_func(newvals[i], 0, &normalized, ++ check_private); ++ if (rc != 0) ++ goto out; ++ } ++ ++ newval = normalized != NULL ? normalized : newvals[i]; ++ if (value == NULL) ++ changedval = str_list_add("", newval); ++ else ++ changedval = str_list_add(value, newval); ++ if (changedval == NULL) { ++ warnx("The %s '%s' is already specified or contains " ++ "invalid characters", msg_obj, newval); ++ rc = -EEXIST; ++ goto out; ++ } ++ if (normalized != NULL) ++ free(normalized); ++ normalized = NULL; ++ free(value); ++ value = changedval; ++ } ++ ++ rc = properties_set(key_props, property, value != NULL ? value : ""); ++ if (rc != 0) ++ warnx("Invalid characters in %ss", msg_obj); ++ ++out: ++ if (newvals != NULL) ++ str_list_free_string_array(newvals); ++ if (value != NULL) ++ free(value); ++ if (normalized != NULL) ++ free(normalized); ++ return rc; ++} ++ ++/** ++ * Add a value to an association property. For each object added function ++ * check_func is called (if not NULL). ++ * ++ * @param[in/out] key_props the properties object to modify ++ * @param[in] property the name of the property to modify ++ * @param[in] newvalue the new value(s) to add, remove or set ++ * @param[in] msg_obj the name of the object for error messages ++ * @param[in] check_func a function to call on each object before it is ++ * added, removed or set to the property ++ * @param[in] check_private a private pointer passed to check_func ++ * ++ * @returns 0 for success, or a negative errno value in case of an error, or ++ * whatever check_func returns if check_func returns a non-zero value. ++ */ ++static int _keystore_add_association(struct properties *key_props, ++ const char *property, ++ const char *newvalue, ++ const char *msg_obj, ++ check_association_t check_func, ++ void *check_private) ++{ ++ char *normalized = NULL; ++ char **newvals = NULL; ++ char *changedval; ++ char *newval; ++ int i, rc = 0; ++ char *value; ++ ++ value = properties_get(key_props, property); ++ if (value == NULL) ++ return _keystore_set_association(key_props, property, ++ newvalue, msg_obj, ++ check_func, check_private); ++ ++ newvals = str_list_split(newvalue); ++ if (newvals == NULL) { ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ for (i = 0; newvals[i] != NULL; i++) { ++ if (check_func != NULL) { ++ rc = check_func(newvals[i], 0, &normalized, ++ check_private); ++ if (rc != 0) ++ goto out; ++ } ++ ++ newval = normalized != NULL ? normalized : newvals[i]; ++ changedval = str_list_add(value, newval); ++ if (changedval == NULL) { ++ warnx("The %s '%s' is already associated with this key " ++ "or contains invalid characters", msg_obj, ++ newval); ++ rc = -EEXIST; ++ goto out; ++ } ++ if (normalized != NULL) ++ free(normalized); ++ normalized = NULL; ++ free(value); ++ value = changedval; ++ } ++ ++ rc = properties_set(key_props, property, value); ++ if (rc != 0) ++ warnx("Invalid characters in %ss", msg_obj); ++ ++out: ++ if (newvals != NULL) ++ str_list_free_string_array(newvals); ++ if (value != NULL) ++ free(value); ++ if (normalized != NULL) ++ free(normalized); ++ return rc; ++} ++ ++/** ++ * Removes a value from an association property. For each object removed ++ * function check_func is called (if not NULL). ++ * ++ * @param[in/out] key_props the properties object to modify ++ * @param[in] property the name of the property to modify ++ * @param[in] delvalue the value(s) to remove ++ * @param[in] msg_obj the name of the object for error messages ++ * @param[in] check_func a function to call on each object before it is ++ * added, removed or set to the property ++ * @param[in] check_private a private pointer passed to check_func ++ * ++ * @returns 0 for success, or a negative errno value in case of an error, or ++ * whatever check_func returns if check_func returns a non-zero value. ++ */ ++static int _keystore_remove_association(struct properties *key_props, ++ const char *property, ++ const char *delvalue, ++ const char *msg_obj, ++ check_association_t check_func, ++ void *check_private) ++{ ++ char *normalized = NULL; ++ char **delvals = NULL; ++ char *changedval; ++ char *delval; ++ int i, rc = 0; ++ char *value; ++ ++ value = properties_get(key_props, property); ++ if (value == NULL) { ++ warnx("No %ss are currently associated with this key", msg_obj); ++ return -ENOENT; ++ } ++ ++ delvals = str_list_split(delvalue); ++ if (delvals == NULL) { ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ for (i = 0; delvals[i] != NULL; i++) { ++ if (check_func != NULL) { ++ rc = check_func(delvals[i], 1, &normalized, ++ check_private); ++ if (rc != 0) ++ goto out; ++ } ++ ++ delval = normalized != NULL ? normalized : delvals[i]; ++ changedval = str_list_remove(value, delval); ++ if (changedval == NULL) { ++ warnx("%s '%s' is not associated with this key", ++ msg_obj, delval); ++ rc = -ENOENT; ++ goto out; ++ } ++ if (normalized != NULL) ++ free(normalized); ++ normalized = NULL; ++ free(value); ++ value = changedval; ++ } ++ ++ rc = properties_set(key_props, property, value); ++ if (rc != 0) ++ warnx("Invalid characters in %ss", msg_obj); ++ ++out: ++ if (delvals != NULL) ++ str_list_free_string_array(delvals); ++ if (value != NULL) ++ free(value); ++ if (normalized != NULL) ++ free(normalized); ++ return rc; ++} ++ ++/** ++ * Change an association property. This function adds the objects in the ++ * comma separated string when newvalue begins with a '+'. It removes the ++ * objects when newvalue begins with a '-', or it sets the property to ++ * newvalue when newvalue does not begin with '+' or '-'. For each object ++ * added, Removed or set function check_func is called (if not NULL). ++ * ++ * @param[in/out] key_props the properties object to modify ++ * @param[in] property the name of the property to modify ++ * @param[in] newvalue the new value(s) to add, remove or set ++ * @param[in] msg_obj the name of the object for error messages ++ * @param[in] check_func a function to call on each object before it is ++ * added, removed or set to the property ++ * @param[in] check_private a private pointer passed to check_func ++ * ++ * @returns 0 for success, or a negative errno value in case of an error, or ++ * whatever check_func returns if check_func returns a non-zero value. ++ */ ++static int _keystore_change_association(struct properties *key_props, ++ const char *property, ++ const char *newvalue, ++ const char *msg_obj, ++ check_association_t check_func, ++ void *check_private) ++{ ++ switch (*newvalue) { ++ case '+': ++ return _keystore_add_association(key_props, property, ++ &newvalue[1], msg_obj, ++ check_func, check_private); ++ case '-': ++ return _keystore_remove_association(key_props, property, ++ &newvalue[1], msg_obj, ++ check_func, check_private); ++ default: ++ return _keystore_set_association(key_props, property, ++ newvalue, msg_obj, ++ check_func, check_private); ++ ++ } ++} ++ ++/** ++ * Filter match function for APQNs ++ * ++ * @param[in] pattern the pattern to match ++ * @param[in] apqn the apqn to match ++ * @param[in] flags Not used here ++ * ++ * @returns Zero if string matches pattern, FNM_NOMATCH if there is no match ++ * or another nonzero value if there is an error. ++ */ ++static int _keystore_apqn_match(const char *pattern, const char *apqn, ++ int UNUSED(flags)) ++{ ++ char *modified; ++ char *pattern_domain; ++ char *pattern_card; ++ char *copy; ++ int card, domain; ++ size_t i; ++ char *ch; ++ int rc; ++ ++ if (sscanf(pattern, "%x.%x", &card, &domain) == 2) { ++ util_asprintf(&modified, "%02x.%04x", card, domain); ++ goto match; ++ } ++ ++ copy = util_strdup(pattern); ++ ++ ch = strchr(copy, '.'); ++ if (ch != NULL) { ++ *ch = '\0'; ++ pattern_card = copy; ++ pattern_domain = ch + 1; ++ ++ modified = NULL; ++ if (strchr(pattern_card, '*') == NULL && ++ strlen(pattern_card) < 2) { ++ for (i = 0; i < 2 - strlen(pattern_card); i++) ++ modified = util_strcat_realloc(modified, "0"); ++ } ++ modified = util_strcat_realloc(modified, pattern_card); ++ ++ modified = util_strcat_realloc(modified, "."); ++ ++ if (strchr(pattern_domain, '*') == NULL && ++ strlen(pattern_domain) < 4) { ++ for (i = 0; i < 4 - strlen(pattern_domain); i++) ++ modified = util_strcat_realloc(modified, "0"); ++ } ++ modified = util_strcat_realloc(modified, pattern_domain); ++ } else { ++ modified = util_strdup(copy); ++ } ++ free(copy); ++ ++match: ++ rc = fnmatch(modified, apqn, FNM_CASEFOLD); ++ ++ free(modified); ++ return rc; ++} ++ ++typedef int (*filter_match_t)(const char *pattern, const char *string, ++ int flags); ++ ++/* ++ * Checks if the value matches the filter list. The value can be a comma ++ * separated string. ++ * ++ * If the filter values contain a second part separated by a colon (':'), then ++ * the filter matches only if both parts match. If the filter values do not ++ * contain a second part,then only the first part is checked, and the second ++ * parts of the values are ignored. ++ * ++ * @param[in] value the value to check ++ * @param[in] filter_list a list of filter strings to match the value with ++ * @param[in] match_func the filter match function. If NULL fnmatch() is used. ++ * ++ * @returns 1 for a match, 0 for not matched ++ */ ++static int _keystore_match_filter(const char *value, ++ char **filter_list, ++ filter_match_t match_func) ++{ ++ char **value_list; ++ int i, k, rc = 0; ++ char *ch; ++ ++ if (filter_list == NULL) ++ return 1; ++ ++ if (match_func == NULL) ++ match_func = fnmatch; ++ ++ value_list = str_list_split(value); ++ for (i = 0; filter_list[i] != NULL && rc == 0; i++) { ++ for (k = 0; value_list[k] != NULL; k++) { ++ /* ++ * Ignore part after ':' of value if filter does ++ * not also contain a ':' part. ++ */ ++ if (strchr(filter_list[i], ':') == NULL) { ++ ch = strchr(value_list[k], ':'); ++ if (ch != NULL) ++ *ch = '\0'; ++ } ++ ++ if (match_func(filter_list[i], value_list[k], 0) == 0) { ++ rc = 1; ++ break; ++ } ++ } ++ } ++ ++ str_list_free_string_array(value_list); ++ return rc; ++} ++ ++/* ++ * Checks if the property value matches the filter list. The property value ++ * can be a comma separated string. ++ * ++ * If the filter values contain a second part separated by a colon (':'), then ++ * the filter matches only if both parts match. If the filter values do not ++ * contain a second part,then only the first part is checked, and the second ++ * parts of the values are ignored. ++ * ++ * @param[in] properties a properties object ++ * @param[in] property the name of the property to check ++ * @param[in] filter_list a list of filter strings to match the value with ++ * @param[in] match_func the filter match function. If NULL fnmatch() is used. ++ * ++ * @returns 1 for a match, 0 for not matched ++ */ ++static int _keystore_match_filter_property(struct properties *properties, ++ const char *property, ++ char **filter_list, ++ filter_match_t match_func) ++{ ++ char *value; ++ int rc; ++ ++ if (filter_list == NULL) ++ return 1; ++ ++ value = properties_get(properties, property); ++ if (value == NULL) ++ return 0; ++ ++ rc = _keystore_match_filter(value, filter_list, match_func); ++ ++ free(value); ++ return rc; ++} ++ ++/** ++ * Checks if a key name matches a name filter ++ * ++ * @param[in] name the name to check ++ * @param[in] name_filter the name filter to match against ++ * ++ * @returns 1 if the filter matches, 0 otherwise ++ */ ++static int _keystore_match_name_filter(const char *name, ++ const char *name_filter) ++{ ++ if (name_filter == NULL) ++ return 1; ++ ++ if (fnmatch(name_filter, name, 0) != 0) ++ return 0; ++ ++ return 1; ++} ++ ++/** ++ * Filters directory entries for scanfile(). Only entries that are regular ++ * files and who's name ends with '.info' are matched. ++ */ ++static int _keystore_info_file_filter(const struct dirent *dirent) ++{ ++ size_t len; ++ ++ if (dirent->d_type != DT_REG) ++ return 0; ++ ++ len = strlen(dirent->d_name); ++ if (len > FILE_EXTENSION_LEN && ++ strcmp(&dirent->d_name[len - FILE_EXTENSION_LEN], ++ INFO_FILE_EXTENSION) == 0) ++ return 1; ++ ++ return 0; ++} ++ ++typedef int (*process_key_t)(struct keystore *keystore, ++ const char *name, struct properties *properties, ++ struct key_filenames *file_names, void *private); ++ ++/** ++ * Iterates over all keys stored in the keystore. For every key that matches ++ * the specified filter process_func is called. ++ * ++ * @param[in] keystore the key store ++ * @param[in] name_filter the name filter. Can contain wild cards. ++ * NULL means no name filter. ++ * @param[in] volume_filter the volume filter. Can contain wild cards, and ++ * mutliple volume filters separated by commas. ++ * If the filter does not contain the ':dm-name' part, ++ * then the volumes are matched without the dm-name ++ * part. If the filter contains the ':dm-name' part, ++ * then the filter is matched including the dm-name ++ * part. ++ * NULL means no volume filter. ++ * specification is ignored for filter matching. ++ * @param[in] apqn_filter the APQN filter. Can contain wild cards, and ++ * mutliple APQN filters separated by commas. ++ * NULL means no APQN filter. ++ * @param[in] process_func the callback function called for a matching key ++ * @param[in/out] process_private private data passed to the process_func ++ * ++ * @returns 0 for success, or a negative errno value in case of an error, or ++ * whatever process_func returns if process_func returns a non-zero ++ * value. ++ */ ++static int _keystore_process_filtered(struct keystore *keystore, ++ const char *name_filter, ++ const char *volume_filter, ++ const char *apqn_filter, ++ process_key_t process_func, ++ void *process_private) ++{ ++ struct key_filenames file_names = { NULL, NULL, NULL }; ++ char **apqn_filter_list = NULL; ++ char **vol_filter_list = NULL; ++ struct properties *key_props; ++ struct dirent **namelist; ++ int n, i, rc = 0; ++ bool skip = 0; ++ char *name; ++ int len; ++ ++ pr_verbose(keystore, "Process_filtered: name_filter = '%s', " ++ "volume_filter = '%s', apqn_filter = '%s'", name_filter, ++ volume_filter, apqn_filter); ++ ++ if (volume_filter != NULL) ++ vol_filter_list = str_list_split(volume_filter); ++ if (apqn_filter != NULL) ++ apqn_filter_list = str_list_split(apqn_filter); ++ ++ n = scandir(keystore->directory, &namelist, _keystore_info_file_filter, ++ alphasort); ++ if (n == -1) { ++ rc = -errno; ++ pr_verbose(keystore, "scandir failed with: %s", strerror(-rc)); ++ return rc; ++ } ++ ++ for (i = 0; i < n ; i++) { ++ if (skip) ++ goto free; ++ ++ name = namelist[i]->d_name; ++ len = strlen(name); ++ if (len > FILE_EXTENSION_LEN) ++ name[len - FILE_EXTENSION_LEN] = '\0'; ++ ++ if (_keystore_match_name_filter(name, name_filter) == 0) { ++ pr_verbose(keystore, ++ "Key '%s' filtered out due to name filter", ++ name); ++ goto free; ++ } ++ ++ rc = _keystore_get_key_filenames(keystore, name, &file_names); ++ if (rc != 0) ++ goto free; ++ ++ rc = _keystore_ensure_keyfiles_exist(&file_names, name); ++ if (rc != 0) ++ goto free_names; ++ ++ key_props = properties_new(); ++ rc = properties_load(key_props, file_names.info_filename, 1); ++ if (rc != 0) { ++ warnx("Key '%s' does not exist or is invalid", name); ++ goto free_prop; ++ } ++ ++ rc = _keystore_match_filter_property(key_props, ++ PROP_NAME_VOLUMES, ++ vol_filter_list, NULL); ++ if (rc == 0) { ++ pr_verbose(keystore, ++ "Key '%s' filtered out due to volumes filter", ++ name); ++ goto free_prop; ++ } ++ ++ rc = _keystore_match_filter_property(key_props, ++ PROP_NAME_APQNS, ++ apqn_filter_list, ++ _keystore_apqn_match); ++ if (rc == 0) { ++ pr_verbose(keystore, ++ "Key '%s' filtered out due to APQN filter", ++ name); ++ goto free_prop; ++ } ++ ++ rc = process_func(keystore, name, key_props, &file_names, ++ process_private); ++ if (rc != 0) { ++ pr_verbose(keystore, "Process function returned %d", ++ rc); ++ skip = 1; ++ } ++ ++free_prop: ++ properties_free(key_props); ++free_names: ++ _keystore_free_key_filenames(&file_names); ++free: ++ free(namelist[i]); ++ } ++ free(namelist); ++ ++ if (vol_filter_list) ++ str_list_free_string_array(vol_filter_list); ++ if (apqn_filter_list) ++ str_list_free_string_array(apqn_filter_list); ++ ++ pr_verbose(keystore, "Process_filtered rc = %d", rc); ++ return rc; ++} ++ ++/** ++ * Checks if the specified APQN is of type CCA and is online ++ * ++ * @param[in] card card number ++ * @param[in] domain the domain ++ * ++ * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its ++ * not a CCA card. ++ */ ++static int _keystore_is_apqn_online(int card, int domain) ++{ ++ long int online; ++ char *dev_path; ++ char type[20]; ++ int rc = 1; ++ ++ dev_path = util_path_sysfs("bus/ap/devices/card%02x", card); ++ if (!util_path_is_dir(dev_path)) { ++ rc = 0; ++ goto out; ++ } ++ if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) { ++ rc = 0; ++ goto out; ++ } ++ if (online == 0) { ++ rc = 0; ++ goto out; ++ } ++ if (util_file_read_line(type, sizeof(type), "%s/type", dev_path) != 0) { ++ rc = 0; ++ goto out; ++ } ++ if (strncmp(type, "CEX", 3) != 0 || strlen(type) < 5) { ++ rc = 0; ++ goto out; ++ } ++ if (type[4] != 'C') { ++ rc = -1; ++ goto out; ++ } ++ free(dev_path); ++ ++ dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x", card, ++ card, domain); ++ if (!util_path_is_dir(dev_path)) { ++ rc = 0; ++ goto out; ++ } ++ if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) { ++ rc = 0; ++ goto out; ++ } ++ if (online == 0) { ++ rc = 0; ++ goto out; ++ } ++ ++out: ++ free(dev_path); ++ return rc; ++} ++ ++/** ++ * Checks an APQN value for its syntax. This is a callback function for ++ * function _keystore_change_association(). ++ * ++ * @param[in] apqn the APQN value to check ++ * @param[in] remove if true the apqn is removed ++ * @param[out] normalized normalized value on return or NULL if no change ++ * @param[in] private private data (not used here) ++ * ++ * @returns 0 if successful, a negative errno value otherwise ++ */ ++static int _keystore_apqn_check(const char *apqn, bool remove, ++ char **normalized, void *UNUSED(private)) ++{ ++ int rc, card, domain; ++ regmatch_t pmatch[1]; ++ regex_t reg_buf; ++ ++ *normalized = NULL; ++ ++ rc = regcomp(®_buf, "[[:xdigit:]]+\\.[[:xdigit:]]", REG_EXTENDED); ++ if (rc != 0) ++ return -EIO; ++ ++ rc = regexec(®_buf, apqn, (size_t) 1, pmatch, 0); ++ if (rc != 0) { ++ warnx("the APQN '%s' is not valid", apqn); ++ return -EINVAL; ++ } ++ ++ if (sscanf(apqn, "%x.%x", &card, &domain) != 2) ++ return -EINVAL; ++ ++ util_asprintf(normalized, "%02x.%04x", card, domain); ++ ++ if (remove) ++ return 0; ++ ++ rc = _keystore_is_apqn_online(card, domain); ++ if (rc != 1) { ++ warnx("The APQN %02x.%04x is %s", card, domain, ++ rc == -1 ? "not a CCA card" : "not online"); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++ ++struct volume_check { ++ struct keystore *keystore; ++ const char *name; ++ const char *volume; ++}; ++ ++/** ++ * Processing callback function for the volume association check function. ++ * ++ * @param[in] keystore the keystore (not used here) ++ * @param[in] name the name of the key ++ * @param[in] properties the properties object of the key (not used here) ++ * @param[in] file_names the file names used by this key (not used here) ++ * @param[in] private private data: struct volume_check ++ * ++ * @returns 0 if the key name is equal to the key we are checking the volume ++ * associations for, -EINVAL otherwise (i.e. to indicate duplicate ++ * volume association) ++ */ ++static int _keystore_volume_check_process(struct keystore *UNUSED(keystore), ++ const char *name, ++ struct properties *UNUSED(properties), ++ struct key_filenames ++ *UNUSED(file_names), ++ void *private) ++{ ++ struct volume_check *info = (struct volume_check *)private; ++ ++ warnx("Key '%s' is already associated with volume '%s'", name, ++ info->volume); ++ return -EINVAL; ++} ++ ++/** ++ * Checks if the volume is a block device ++ * ++ * @param[in] volume the volume to check ++ * ++ * @return 1 if the volume is a block device, 0 otherwise ++ */ ++static int _keystore_is_block_device(const char *volume) ++{ ++ struct stat sb; ++ ++ if (stat(volume, &sb)) ++ return 0; ++ if (!S_ISBLK(sb.st_mode)) ++ return 0; ++ ++ return 1; ++} ++ ++/** ++ * Checks an Volume value for its syntax and if it is already associated with ++ * another key. This is a callback function for function ++ * _keystore_change_association(). ++ * ++ * @param[in] volume the Volume value to check ++ * @param[in] remove if true the volume is removed ++ * @param[out] normalized normalized value on return or NULL if no change ++ * @param[in] private private data: struct volume_check ++ * ++ * @returns 0 if successful, a negative errno value otherwise ++ */ ++static int _keystore_volume_check(const char *volume, bool remove, ++ char **normalized, void *private) ++{ ++ struct volume_check *info = (struct volume_check *)private; ++ char *ch; ++ int rc; ++ ++ *normalized = NULL; ++ ++ if (strpbrk(volume, "*?") != NULL) { ++ warnx("Volume name can not contain '*' or '?'"); ++ return -EINVAL; ++ } ++ ++ info->volume = util_strdup(volume); ++ ch = strchr(info->volume, ':'); ++ if (ch == NULL || strlen(ch + 1) == 0) { ++ warnx("Volume specification must contain a dm-crypt mapping " ++ "name separated by a colon"); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ if (remove) { ++ rc = 0; ++ goto out; ++ } ++ ++ /* ++ * Strip off the ':dm-name' part, so that the volume filter only ++ * matches the volume part. ++ */ ++ *ch = '\0'; ++ ++ if (!_keystore_is_block_device(info->volume)) { ++ warnx("Volume '%s' is not a block device or is not available", ++ info->volume); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ rc = _keystore_process_filtered(info->keystore, NULL, info->volume, ++ NULL, _keystore_volume_check_process, ++ info); ++out: ++ free((void *)info->volume); ++ info->volume = NULL; ++ return rc; ++} ++ ++/** ++ * Locks the repository against other processes. ++ * ++ * @param[in] keystore the keystore ++ * ++ * @returns 0 if successful, a negative errno value otherwise ++ */ ++static int _keystore_lock_repository(struct keystore *keystore) ++{ ++ char *lock_file_name; ++ struct stat sb; ++ int rc; ++ ++ util_asprintf(&lock_file_name, "%s/%s", keystore->directory, ++ LOCK_FILE_NAME); ++ ++ if (stat(lock_file_name, &sb) == 0) { ++ keystore->lock_fd = open(lock_file_name, O_RDONLY); ++ if (keystore->lock_fd == -1) { ++ rc = -errno; ++ warnx("Failed to open lock file '%s': %s", ++ lock_file_name, ++ strerror(-rc)); ++ goto out; ++ } ++ } else { ++ keystore->lock_fd = open(lock_file_name, O_CREAT | O_RDONLY, ++ keystore->mode); ++ if (keystore->lock_fd == -1) { ++ rc = -errno; ++ warnx("Failed to create lock file '%s': %s", ++ lock_file_name, ++ strerror(-rc)); ++ goto out; ++ } ++ ++ if (fchown(keystore->lock_fd, geteuid(), ++ keystore->owner) != 0) { ++ rc = -errno; ++ warnx("chown faild on file '%s': %s", lock_file_name, ++ strerror(-rc)); ++ return rc; ++ } ++ } ++ ++ rc = flock(keystore->lock_fd, LOCK_EX); ++ if (rc == -1) { ++ rc = -errno; ++ warnx("Failed to obtain the file lock on '%s': %s", ++ lock_file_name, strerror((-rc))); ++ } ++ ++out: ++ free(lock_file_name); ++ return rc; ++} ++ ++/** ++ * Unlocks the repository ++ * ++ * @param[in] keystore the keystore ++ * ++ * @returns 0 if successful, a negative errno value otherwise ++ */ ++static int _keystore_unlock_repository(struct keystore *keystore) ++{ ++ int rc; ++ ++ if (keystore->lock_fd == -1) ++ return 0; ++ ++ rc = flock(keystore->lock_fd, LOCK_UN); ++ if (rc == -1) { ++ rc = -errno; ++ warnx("Failed to release the file lock: %s", strerror((-rc))); ++ } ++ ++ close(keystore->lock_fd); ++ keystore->lock_fd = -1; ++ ++ return rc; ++} ++ ++/** ++ * Allocates new keystore object ++ * ++ * @param[in] directory the directory where the keystore resides ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns a new keystore object ++ */ ++struct keystore *keystore_new(const char *directory, bool verbose) ++{ ++ struct keystore *keystore; ++ struct stat sb; ++ int rc; ++ ++ util_assert(directory != NULL, "Internal error: directory is NULL"); ++ ++ if (stat(directory, &sb) != 0) { ++ warnx("'%s' does not exist", directory); ++ return NULL; ++ } ++ if (!(sb.st_mode & S_IFDIR)) { ++ warnx("'%s' is not a directory", directory); ++ return NULL; ++ } ++ if (!util_path_is_readable(directory) || ++ !util_path_is_writable(directory)) { ++ warnx("Permission denied for '%s'", directory); ++ return NULL; ++ } ++ if (sb.st_mode & S_IWOTH) { ++ warnx("Directory '%s' is writable for others, this is not " ++ "accepted", directory); ++ return NULL; ++ } ++ ++ keystore = util_zalloc(sizeof(struct keystore)); ++ ++ keystore->owner = sb.st_gid; ++ keystore->mode = sb.st_mode & (S_IRUSR | S_IWUSR | ++ S_IRGRP | S_IWGRP | ++ S_IROTH); ++ keystore->lock_fd = -1; ++ keystore->verbose = verbose; ++ keystore->directory = util_strdup(directory); ++ if (keystore->directory[strlen(keystore->directory)-1] == '/') ++ keystore->directory[strlen(keystore->directory)-1] = '\0'; ++ ++ rc = _keystore_lock_repository(keystore); ++ if (rc != 0) { ++ keystore_free(keystore); ++ return NULL; ++ } ++ ++ pr_verbose(keystore, "Keystore in directory '%s' opened successfully", ++ keystore->directory); ++ return keystore; ++} ++ ++/** ++ * Sets a timestamp to be used as creation/update/reencipher time into ++ * the specified property ++ * ++ * @param[in] properties the properties object ++ * @param[in] property the name of the property to set ++ * ++ * @returns 0 on success, or a negative errno value on error ++ */ ++static int _keystore_set_timestamp_property(struct properties *properties, ++ const char *property) ++{ ++ char *time_str; ++ struct tm *tm; ++ time_t t; ++ int rc; ++ ++ t = time(NULL); ++ tm = localtime(&t); ++ util_assert(tm != NULL, "Internal error: tm is NULL"); ++ ++ time_str = util_zalloc(200); ++ rc = strftime(time_str, 200, "%F %T", tm); ++ util_assert(rc > 0, "Internal error: strftime failed"); ++ ++ rc = properties_set(properties, property, time_str); ++ ++ free(time_str); ++ return rc; ++} ++ ++/** ++ * Sets the default properties of a key, such as key-type, cipher-name, and ++ * IV-mode ++ * ++ * @param[in] key_props the properties object ++ */ ++static int _keystore_set_default_properties(struct properties *key_props) ++{ ++ int rc; ++ ++ rc = properties_set(key_props, PROP_NAME_KEY_TYPE, "CCA-AESDATA"); ++ if (rc != 0) ++ return rc; ++ ++ rc = properties_set(key_props, PROP_NAME_CIPHER, "paes"); ++ if (rc != 0) ++ return rc; ++ ++ rc = properties_set(key_props, PROP_NAME_IV_MODE, "plain64"); ++ if (rc != 0) ++ return rc; ++ ++ rc = _keystore_set_timestamp_property(key_props, ++ PROP_NAME_CREATION_TIME); ++ if (rc != 0) ++ return rc; ++ ++ return 0; ++} ++ ++/** ++ * Creates an initial .info file for a key ++ * ++ * @param[in] keystore the key store ++ * @param[in] name the name of the key ++ * @param[in] info_filename the file name of the key info file ++ * @param[in] description textual description of the key (optional, can be NULL) ++ * @param[in] volumes a comma separated list of volumes associated with this ++ * key (optional, can be NULL) ++ * @param[in] apqns a comma separated list of APQNs associated with this ++ * key (optional, can be NULL) ++ * @param[in] sector_size the sector size to use with dm-crypt. It must be power ++ * of two and in range 512 - 4096 bytes. 0 means that ++ * the sector size is not specified and the system ++ * default is used. ++ */ ++static int _keystore_create_info_file(struct keystore *keystore, ++ const char *name, ++ const char *info_filename, ++ const char *description, ++ const char *volumes, const char *apqns, ++ size_t sector_size) ++{ ++ struct volume_check vol_check = { .keystore = keystore, .name = name }; ++ struct properties *key_props; ++ char temp[10]; ++ int rc; ++ ++ key_props = properties_new(); ++ rc = _keystore_set_default_properties(key_props); ++ if (rc != 0) ++ goto out; ++ ++ rc = properties_set(key_props, PROP_NAME_DESCRIPTION, ++ description != NULL ? description : ""); ++ if (rc != 0) { ++ warnx("Invalid characters in description"); ++ goto out; ++ } ++ ++ rc = _keystore_change_association(key_props, PROP_NAME_VOLUMES, ++ volumes != NULL ? volumes : "", ++ "volume", _keystore_volume_check, ++ &vol_check); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_change_association(key_props, PROP_NAME_APQNS, ++ apqns != NULL ? apqns : "", ++ "APQN", _keystore_apqn_check, NULL); ++ if (rc != 0) ++ goto out; ++ ++ if (!_keystore_valid_sector_size(sector_size)) { ++ warnx("Invalid sector-size specified"); ++ rc = -EINVAL; ++ goto out; ++ } ++ sprintf(temp, "%lu", sector_size); ++ rc = properties_set(key_props, PROP_NAME_SECTOR_SIZE, ++ temp); ++ if (rc != 0) { ++ warnx("Invalid characters in sector-size"); ++ goto out; ++ } ++ ++ rc = properties_save(key_props, info_filename, 1); ++ if (rc != 0) { ++ pr_verbose(keystore, ++ "Key info file '%s' could not be written: %s", ++ info_filename, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = _keystore_set_file_permission(keystore, info_filename); ++ if (rc != 0) { ++ remove(info_filename); ++ goto out; ++ } ++ ++out: ++ properties_free(key_props); ++ return rc; ++} ++ ++/** ++ * Extracts a card/domain pair from the specified APQns, or uses AUTOSELECT ++ * if no APQNs are specified. ++ */ ++static int _keystore_get_card_domain(const char *apqns, unsigned int *card, ++ unsigned int *domain) ++{ ++ char **apqn_list; ++ char *normalized = NULL; ++ int rc = 0; ++ ++ *card = AUTOSELECT; ++ *domain = AUTOSELECT; ++ ++ if (apqns == NULL) ++ return 0; ++ ++ apqn_list = str_list_split(apqns); ++ if (apqn_list[0] == NULL) ++ goto out; ++ ++ rc = _keystore_apqn_check(apqn_list[0], 0, &normalized, NULL); ++ if (normalized != NULL) ++ free(normalized); ++ if (rc != 0) ++ goto out; ++ ++ if (sscanf(apqn_list[0], "%x.%x", card, domain) != 2) { ++ rc = -EINVAL; ++ goto out; ++ } ++ ++out: ++ str_list_free_string_array(apqn_list); ++ return rc; ++} ++ ++/** ++ * Generates a secure key by random and adds it to the key store ++ * ++ * @param[in] keystore the key store ++ * @param[in] name the name of the key ++ * @param[in] description textual description of the key (optional, can be NULL) ++ * @param[in] volumes a comma separated list of volumes associated with this ++ * key (optional, can be NULL) ++ * @param[in] apqns a comma separated list of APQNs associated with this ++ * key (optional, can be NULL) ++ * @param[in] sector_size the sector size to use with dm-crypt. It must be power ++ * of two and in range 512 - 4096 bytes. 0 means that ++ * the sector size is not specified and the system ++ * default is used. ++ * @param[in] keybits cryptographical size of the key in bits ++ * @param[in] xts if true, an XTS key is generated ++ * @param[in] clear_key_file if not NULL the secure key is generated from the ++ * clear key contained in the file denoted here. ++ * if NULL, the secure key is generated by random. ++ * @param[in] pkey_fd the file descriptor of /dev/pkey ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int keystore_generate_key(struct keystore *keystore, const char *name, ++ const char *description, const char *volumes, ++ const char *apqns, size_t sector_size, ++ size_t keybits, bool xts, const char *clear_key_file, ++ int pkey_fd) ++{ ++ struct key_filenames file_names = { NULL, NULL, NULL }; ++ struct properties *key_props = NULL; ++ unsigned int card, domain; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ util_assert(name != NULL, "Internal error: name is NULL"); ++ ++ rc = _keystore_get_key_filenames(keystore, name, &file_names); ++ if (rc != 0) ++ goto out_free_key_filenames; ++ ++ rc = _keystore_ensure_keyfiles_not_exist(&file_names, name); ++ if (rc != 0) ++ goto out_free_key_filenames; ++ ++ rc = _keystore_get_card_domain(apqns, &card, &domain); ++ if (rc != 0) ++ goto out_free_key_filenames; ++ ++ if (clear_key_file == NULL) ++ rc = generate_secure_key_random(pkey_fd, ++ file_names.skey_filename, ++ keybits, xts, card, domain, ++ keystore->verbose); ++ else ++ rc = generate_secure_key_clear(pkey_fd, ++ file_names.skey_filename, ++ keybits, xts, clear_key_file, ++ card, domain, ++ keystore->verbose); ++ if (rc != 0) ++ goto out_free_props; ++ ++ rc = _keystore_set_file_permission(keystore, file_names.skey_filename); ++ if (rc != 0) ++ goto out_free_props; ++ ++ rc = _keystore_create_info_file(keystore, name, ++ file_names.info_filename, ++ description, volumes, apqns, ++ sector_size); ++ if (rc != 0) ++ goto out_free_props; ++ ++ pr_verbose(keystore, ++ "Successfully generated a secure key in '%s' and key info " ++ "in '%s'", file_names.skey_filename, ++ file_names.info_filename); ++ ++out_free_props: ++ if (key_props != NULL) ++ properties_free(key_props); ++ if (rc != 0 && rc != -EEXIST) ++ remove(file_names.skey_filename); ++out_free_key_filenames: ++ _keystore_free_key_filenames(&file_names); ++ ++ if (rc != 0) ++ pr_verbose(keystore, "Failed to generate key '%s': %s", ++ name, strerror(-rc)); ++ return rc; ++} ++ ++/** ++ * Imports a secure key from a file and adds it to the key store ++ * ++ * @param[in] keystore the key store ++ * @param[in] name the name of the key ++ * @param[in] description textual description of the key (optional, can be NULL) ++ * @param[in] volumes a comma separated list of volumes associated with this ++ * key (optional, can be NULL) ++ * @param[in] apqns a comma separated list of APQNs associated with this ++ * key (optional, can be NULL) ++ * @param[in] sector_size the sector size to use with dm-crypt. It must be power ++ * of two and in range 512 - 4096 bytes. 0 means that ++ * the sector size is not specified and the system ++ * default is used. ++ * @param[in] import_file The name of a secure key containing the kley to import ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int keystore_import_key(struct keystore *keystore, const char *name, ++ const char *description, const char *volumes, ++ const char *apqns, size_t sector_size, ++ const char *import_file) ++{ ++ struct key_filenames file_names = { NULL, NULL, NULL }; ++ struct properties *key_props = NULL; ++ size_t secure_key_size; ++ u8 *secure_key; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ util_assert(name != NULL, "Internal error: name is NULL"); ++ util_assert(import_file != NULL, "Internal error: import_file is NULL"); ++ ++ rc = _keystore_get_key_filenames(keystore, name, &file_names); ++ if (rc != 0) ++ goto out_free_key_filenames; ++ ++ rc = _keystore_ensure_keyfiles_not_exist(&file_names, name); ++ if (rc != 0) ++ goto out_free_key_filenames; ++ ++ secure_key = read_secure_key(import_file, &secure_key_size, ++ keystore->verbose); ++ if (secure_key == NULL) { ++ rc = -ENOENT; ++ goto out_free_key_filenames; ++ } ++ ++ rc = write_secure_key(file_names.skey_filename, secure_key, ++ secure_key_size, keystore->verbose); ++ free(secure_key); ++ if (rc != 0) ++ goto out_free_props; ++ ++ rc = _keystore_set_file_permission(keystore, file_names.skey_filename); ++ if (rc != 0) ++ goto out_free_props; ++ ++ rc = _keystore_create_info_file(keystore, name, ++ file_names.info_filename, ++ description, volumes, apqns, ++ sector_size); ++ if (rc != 0) ++ goto out_free_props; ++ ++ pr_verbose(keystore, ++ "Successfully imported a secure key in '%s' and key info in '%s'", ++ file_names.skey_filename, file_names.info_filename); ++ ++out_free_props: ++ if (key_props != NULL) ++ properties_free(key_props); ++ if (rc != 0 && rc != -EEXIST) ++ remove(file_names.skey_filename); ++out_free_key_filenames: ++ _keystore_free_key_filenames(&file_names); ++ ++ if (rc != 0) ++ pr_verbose(keystore, "Failed to import key '%s': %s", ++ name, strerror(-rc)); ++ return rc; ++} ++ ++ ++/** ++ * Changes properties of a key in the keystore. ++ * ++ * @param[in] keystore the key store ++ * @param[in] name the name of the key ++ * @param[in] description textual description of the key. If NULL then the ++ * description is not changed. ++ * @param[in] volumes a comma separated list of volumes associated with this ++ * key, or a volume prefixed with '+' or '-' to add or ++ * remove that volume respectively. If NULL then the ++ * volumes are not changed. ++ * @param[in] apqns a comma separated list of APQNs associated with this ++ * key, or an APQN prefixed with '+' or '-' to add or ++ * remove that APQN respectively. IfNULL then the APQNs ++ * are not changed. ++ * @param[in] sector_size the sector size to use with dm-crypt. It must be power ++ * of two and in range 512 - 4096 bytes. 0 means that ++ * the sector size is not specified and the system ++ * default is used. Specify -1 if this property should ++ * not be changed. ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ * ++ */ ++int keystore_change_key(struct keystore *keystore, const char *name, ++ const char *description, const char *volumes, ++ const char *apqns, long int sector_size) ++{ ++ struct volume_check vol_check = { .keystore = keystore, .name = name }; ++ struct key_filenames file_names = { NULL, NULL, NULL }; ++ struct properties *key_props = NULL; ++ char temp[10]; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ util_assert(name != NULL, "Internal error: name is NULL"); ++ ++ rc = _keystore_get_key_filenames(keystore, name, &file_names); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_ensure_keyfiles_exist(&file_names, name); ++ if (rc != 0) ++ goto out; ++ ++ key_props = properties_new(); ++ rc = properties_load(key_props, file_names.info_filename, 1); ++ if (rc != 0) { ++ warnx("Key '%s' does not exist or is invalid", name); ++ goto out; ++ } ++ ++ if (description != NULL) { ++ rc = properties_set(key_props, PROP_NAME_DESCRIPTION, ++ description); ++ if (rc != 0) { ++ warnx("Invalid characters in description"); ++ goto out; ++ } ++ } ++ ++ if (volumes != NULL) { ++ rc = _keystore_change_association(key_props, PROP_NAME_VOLUMES, ++ volumes, "volume", ++ _keystore_volume_check, ++ &vol_check); ++ if (rc != 0) ++ goto out; ++ } ++ ++ if (apqns != NULL) { ++ rc = _keystore_change_association(key_props, PROP_NAME_APQNS, ++ apqns, "APQN", ++ _keystore_apqn_check, NULL); ++ if (rc != 0) ++ goto out; ++ } ++ ++ if (sector_size >= 0) { ++ if (!_keystore_valid_sector_size(sector_size)) { ++ warnx("Invalid sector-size specified"); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ sprintf(temp, "%lu", sector_size); ++ rc = properties_set(key_props, PROP_NAME_SECTOR_SIZE, ++ temp); ++ if (rc != 0) { ++ warnx("Invalid characters in sector-size"); ++ goto out; ++ } ++ } ++ ++ rc = _keystore_set_timestamp_property(key_props, PROP_NAME_CHANGE_TIME); ++ if (rc != 0) ++ goto out; ++ ++ rc = properties_save(key_props, file_names.info_filename, 1); ++ if (rc != 0) { ++ pr_verbose(keystore, ++ "Key info file '%s' could not be written: %s", ++ file_names.info_filename, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = _keystore_set_file_permission(keystore, file_names.info_filename); ++ if (rc != 0) ++ goto out; ++ ++ pr_verbose(keystore, "Successfully changed key '%s'", name); ++ ++out: ++ _keystore_free_key_filenames(&file_names); ++ if (key_props != NULL) ++ properties_free(key_props); ++ ++ if (rc != 0) ++ pr_verbose(keystore, "Failed to change key '%s': %s", ++ name, strerror(-rc)); ++ return rc; ++} ++ ++/** ++ * Renames a key in the keystore ++ * ++ * @param[in] keystore the key store ++ * @param[in] name the name of the key ++ * @param[in] newname the new name of the key ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int keystore_rename_key(struct keystore *keystore, const char *name, ++ const char *newname) ++{ ++ struct key_filenames file_names = { NULL, NULL, NULL }; ++ struct key_filenames new_names = { NULL, NULL, NULL }; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ util_assert(name != NULL, "Internal error: name is NULL"); ++ util_assert(newname != NULL, "Internal error: newname is NULL"); ++ ++ rc = _keystore_get_key_filenames(keystore, name, &file_names); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_ensure_keyfiles_exist(&file_names, name); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_get_key_filenames(keystore, newname, &new_names); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_ensure_keyfiles_not_exist(&new_names, newname); ++ if (rc != 0) ++ goto out; ++ ++ if (rename(file_names.skey_filename, new_names.skey_filename) != 0) { ++ rc = -errno; ++ pr_verbose(keystore, "Failed to rename '%s': %s", ++ file_names.skey_filename, strerror(-rc)); ++ goto out; ++ } ++ if (rename(file_names.info_filename, new_names.info_filename) != 0) { ++ rc = -errno; ++ pr_verbose(keystore, "Failed to rename '%s': %s", ++ file_names.info_filename, strerror(-rc)); ++ rename(new_names.skey_filename, file_names.skey_filename); ++ } ++ if (_keystore_reencipher_key_exists(&file_names)) { ++ if (rename(file_names.renc_filename, ++ new_names.renc_filename) != 0) { ++ rc = -errno; ++ pr_verbose(keystore, "Failed to rename '%s': %s", ++ file_names.renc_filename, strerror(-rc)); ++ rename(new_names.skey_filename, ++ file_names.skey_filename); ++ rename(new_names.info_filename, ++ file_names.info_filename); ++ } ++ } ++ ++ pr_verbose(keystore, "Successfully renamed key '%s' to '%s'", name, ++ newname); ++ ++out: ++ _keystore_free_key_filenames(&file_names); ++ _keystore_free_key_filenames(&new_names); ++ ++ if (rc != 0) ++ pr_verbose(keystore, "Failed to rename key '%s'to '%s': %s", ++ name, newname, strerror(-rc)); ++ return rc; ++} ++ ++/** ++ * Sets up a util_rec used for displaying key information ++ * ++ * @param[in] validation if true the record is used for validate, else it is ++ * used for display ++ * ++ * @returns a pointer to a set up struct util_rec. ++ */ ++static struct util_rec *_keystore_setup_record(bool validation) ++{ ++ struct util_rec *rec; ++ ++ rec = util_rec_new_long("-", ":", REC_KEY, 23, 54); ++ util_rec_def(rec, REC_KEY, UTIL_REC_ALIGN_LEFT, 54, REC_KEY); ++ if (validation) ++ util_rec_def(rec, REC_STATUS, UTIL_REC_ALIGN_LEFT, 54, ++ REC_STATUS); ++ util_rec_def(rec, REC_DESCRIPTION, UTIL_REC_ALIGN_LEFT, 54, ++ REC_DESCRIPTION); ++ util_rec_def(rec, REC_SEC_KEY_SIZE, UTIL_REC_ALIGN_LEFT, 20, ++ REC_SEC_KEY_SIZE); ++ util_rec_def(rec, REC_CLR_KEY_SIZE, UTIL_REC_ALIGN_LEFT, 20, ++ REC_CLR_KEY_SIZE); ++ util_rec_def(rec, REC_XTS, UTIL_REC_ALIGN_LEFT, 3, REC_XTS); ++ if (validation) ++ util_rec_def(rec, REC_MASTERKEY, UTIL_REC_ALIGN_LEFT, 54, ++ REC_MASTERKEY); ++ util_rec_def(rec, REC_VOLUMES, UTIL_REC_ALIGN_LEFT, 54, REC_VOLUMES); ++ util_rec_def(rec, REC_APQNS, UTIL_REC_ALIGN_LEFT, 54, REC_APQNS); ++ util_rec_def(rec, REC_KEY_FILE, UTIL_REC_ALIGN_LEFT, 54, REC_KEY_FILE); ++ util_rec_def(rec, REC_SECTOR_SIZE, UTIL_REC_ALIGN_LEFT, 54, ++ REC_SECTOR_SIZE); ++ util_rec_def(rec, REC_CREATION_TIME, UTIL_REC_ALIGN_LEFT, 54, ++ REC_CREATION_TIME); ++ util_rec_def(rec, REC_CHANGE_TIME, UTIL_REC_ALIGN_LEFT, 54, ++ REC_CHANGE_TIME); ++ util_rec_def(rec, REC_REENC_TIME, UTIL_REC_ALIGN_LEFT, 54, ++ REC_REENC_TIME); ++ ++ return rec; ++} ++ ++static void _keystore_print_record(struct util_rec *rec, ++ const char *name, ++ struct properties *properties, ++ bool validation, const char *skey_filename, ++ size_t secure_key_size, ++ size_t clear_key_bitsize, bool valid, ++ bool is_old_mk, bool reenc_pending) ++{ ++ char *volumes_argz = NULL; ++ size_t volumes_argz_len; ++ char *apqns_argz = NULL; ++ size_t sector_size = 0; ++ size_t apqns_argz_len; ++ char *description; ++ char *reencipher; ++ char *creation; ++ char *volumes; ++ char *change; ++ char *apqns; ++ char *temp; ++ ++ description = properties_get(properties, PROP_NAME_DESCRIPTION); ++ volumes = properties_get(properties, PROP_NAME_VOLUMES); ++ if (volumes != NULL) ++ util_assert(argz_create_sep(volumes, ',', ++ &volumes_argz, ++ &volumes_argz_len) == 0, ++ "Internal error: argz_create_sep failed"); ++ apqns = properties_get(properties, PROP_NAME_APQNS); ++ if (apqns != NULL) ++ util_assert(argz_create_sep(apqns, ',', ++ &apqns_argz, ++ &apqns_argz_len) == 0, ++ "Internal error: argz_create_sep failed"); ++ ++ temp = properties_get(properties, PROP_NAME_SECTOR_SIZE); ++ if (temp != NULL) { ++ util_assert(sscanf(temp, "%lu", §or_size) == 1, ++ "Internal error: sscanf failed"); ++ free(temp); ++ } ++ ++ creation = properties_get(properties, PROP_NAME_CREATION_TIME); ++ change = properties_get(properties, PROP_NAME_CHANGE_TIME); ++ reencipher = properties_get(properties, PROP_NAME_REENC_TIME); ++ ++ util_rec_set(rec, REC_KEY, name); ++ if (validation) ++ util_rec_set(rec, REC_STATUS, valid ? "Valid" : "Invalid"); ++ util_rec_set(rec, REC_DESCRIPTION, ++ description != NULL ? description : ""); ++ util_rec_set(rec, REC_SEC_KEY_SIZE, "%lu bytes", secure_key_size); ++ if (!validation || valid) ++ util_rec_set(rec, REC_CLR_KEY_SIZE, "%lu bits", ++ clear_key_bitsize); ++ else ++ util_rec_set(rec, REC_CLR_KEY_SIZE, "(unknown)"); ++ util_rec_set(rec, REC_XTS, ++ IS_XTS(secure_key_size) ? "Yes" : "No"); ++ if (validation) { ++ if (valid) ++ util_rec_set(rec, REC_MASTERKEY, ++ is_old_mk ? "OLD CCA master key" : ++ "CURRENT CCA master key"); ++ else ++ util_rec_set(rec, REC_MASTERKEY, "(unknown)"); ++ } ++ if (volumes_argz != NULL) ++ util_rec_set_argz(rec, REC_VOLUMES, volumes_argz, ++ volumes_argz_len); ++ else ++ util_rec_set(rec, REC_VOLUMES, "(none)"); ++ if (apqns_argz != NULL) ++ util_rec_set_argz(rec, REC_APQNS, ++ apqns_argz, apqns_argz_len); ++ else ++ util_rec_set(rec, REC_APQNS, "(none)"); ++ util_rec_set(rec, REC_KEY_FILE, skey_filename); ++ if (sector_size == 0) ++ util_rec_set(rec, REC_SECTOR_SIZE, "(system default)"); ++ else ++ util_rec_set(rec, REC_SECTOR_SIZE, "%lu bytes", ++ sector_size); ++ util_rec_set(rec, REC_CREATION_TIME, creation); ++ util_rec_set(rec, REC_CHANGE_TIME, ++ change != NULL ? change : "(never)"); ++ util_rec_set(rec, REC_REENC_TIME, "%s %s", ++ reencipher != NULL ? reencipher : "(never)", ++ reenc_pending ? "(re-enciphering pending)" : ""); ++ ++ util_rec_print(rec); ++ ++ if (description != NULL) ++ free(description); ++ if (volumes != NULL) ++ free(volumes); ++ if (volumes_argz != NULL) ++ free(volumes_argz); ++ if (apqns != NULL) ++ free(apqns); ++ if (apqns_argz != NULL) ++ free(apqns_argz); ++ if (creation != NULL) ++ free(creation); ++ if (change != NULL) ++ free(change); ++ if (reencipher != NULL) ++ free(reencipher); ++} ++ ++struct validate_info { ++ struct util_rec *rec; ++ int pkey_fd; ++ unsigned long int num_valid; ++ unsigned long int num_invalid; ++ unsigned long int num_warnings; ++}; ++ ++/** ++ * Displays the status of the associated APQNs. ++ * ++ * @param[in] properties the properties of the key ++ * @param[in] name the name of the key ++ * ++ * @returns 0 in case of success, 1 if at least one of the APQNs is not ++ * available ++ */ ++static int _keystore_display_apqn_status(struct properties *properties, ++ const char *name) ++{ ++ int i, rc, card, domain, warning = 0; ++ char **apqn_list; ++ char *apqns; ++ ++ apqns = properties_get(properties, PROP_NAME_APQNS); ++ if (apqns == NULL) ++ return 0; ++ apqn_list = str_list_split(apqns); ++ ++ for (i = 0; apqn_list[i] != NULL; i++) { ++ ++ if (sscanf(apqn_list[i], "%x.%x", &card, &domain) != 2) ++ continue; ++ ++ rc = _keystore_is_apqn_online(card, domain); ++ if (rc != 1) { ++ printf("WARNING: The APQN %02x.%04x associated with " ++ "key '%s' is %s\n", card, domain, name, ++ rc == -1 ? "not a CCA card" : "not online"); ++ warning = 1; ++ } ++ } ++ ++ if (warning) ++ printf("\n"); ++ ++ free(apqns); ++ str_list_free_string_array(apqn_list); ++ return warning; ++} ++/** ++ * Displays the status of the associated volumes. ++ * ++ * @param[in] properties the properties of the key ++ * @param[in] name the name of the key ++ * ++ * @returns 0 in case of success, 1 if at least one of the volumes is not ++ * available ++ */ ++static int _keystore_display_volume_status(struct properties *properties, ++ const char *name) ++{ ++ int i, warning = 0; ++ char **volume_list; ++ char *volumes; ++ char *ch; ++ ++ volumes = properties_get(properties, PROP_NAME_VOLUMES); ++ if (volumes == NULL) ++ return 0; ++ volume_list = str_list_split(volumes); ++ ++ for (i = 0; volume_list[i] != NULL; i++) { ++ ++ ch = strchr(volume_list[i], ':'); ++ if (ch != NULL) ++ *ch = '\0'; ++ ++ if (!_keystore_is_block_device(volume_list[i])) { ++ printf("WARNING: The volume '%s' associated with " ++ "key '%s' is not available\n", volume_list[i], ++ name); ++ warning = 1; ++ } ++ } ++ ++ if (warning) ++ printf("\n"); ++ ++ free(volumes); ++ str_list_free_string_array(volume_list); ++ return warning; ++} ++ ++/** ++ * Processing function for the key validate function. Prints validation ++ * information for the key to be validated. ++ * ++ * @param[in] keystore the keystore ++ * @param[in] name the name of the key ++ * @param[in] properties the properties object of the key ++ * @param[in] file_names the file names used by this key ++ * @param[in] private private data: struct validate_info ++ * ++ * @returns 0 if the validation is successful, a negative errno value otherwise ++ */ ++static int _keystore_process_validate(struct keystore *keystore, ++ const char *name, ++ struct properties *properties, ++ struct key_filenames *file_names, ++ void *private) ++{ ++ struct validate_info *info = (struct validate_info *)private; ++ size_t clear_key_bitsize; ++ size_t secure_key_size; ++ u8 *secure_key; ++ int is_old_mk; ++ int rc, valid; ++ ++ rc = _keystore_ensure_keyfiles_exist(file_names, name); ++ if (rc != 0) ++ goto out; ++ ++ secure_key = read_secure_key(file_names->skey_filename, ++ &secure_key_size, keystore->verbose); ++ if (secure_key == NULL) { ++ rc = -ENOENT; ++ goto out; ++ } ++ ++ rc = validate_secure_key(info->pkey_fd, secure_key, secure_key_size, ++ &clear_key_bitsize, &is_old_mk, ++ keystore->verbose); ++ if (rc != 0) { ++ valid = 0; ++ info->num_invalid++; ++ rc = 0; ++ } else { ++ info->num_valid++; ++ valid = 1; ++ } ++ free(secure_key); ++ ++ _keystore_print_record(info->rec, name, properties, 1, ++ file_names->skey_filename, secure_key_size, ++ clear_key_bitsize, valid, is_old_mk, ++ _keystore_reencipher_key_exists(file_names)); ++ ++ if (valid && is_old_mk) { ++ util_print_indented("WARNING: The secure key is currently " ++ "enciphered with the OLD CCA master key " ++ "and should be re-enciphered with the " ++ "CURRENT CCA master key as soon as " ++ "possible to avoid data loss\n", 0); ++ info->num_warnings++; ++ } ++ if (_keystore_display_apqn_status(properties, name) != 0) ++ info->num_warnings++; ++ if (_keystore_display_volume_status(properties, name) != 0) ++ info->num_warnings++; ++ ++out: ++ if (rc != 0) ++ pr_verbose(keystore, "Failed to validate key '%s': %s", ++ name, strerror(-rc)); ++ return rc; ++} ++ ++/** ++ * Validates one or multiple keys in the keystore ++ * ++ * @param[in] keystore the key store ++ * @param[in] name_filter the name filter to select the key (can be NULL) ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int keystore_validate_key(struct keystore *keystore, const char *name_filter, ++ const char *apqn_filter, int pkey_fd) ++{ ++ struct validate_info info; ++ struct util_rec *rec; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ ++ rec = _keystore_setup_record(1); ++ ++ info.pkey_fd = pkey_fd; ++ info.rec = rec; ++ info.num_valid = 0; ++ info.num_invalid = 0; ++ info.num_warnings = 0; ++ ++ rc = _keystore_process_filtered(keystore, name_filter, NULL, ++ apqn_filter, ++ _keystore_process_validate, &info); ++ ++ util_rec_free(rec); ++ ++ if (rc != 0) { ++ pr_verbose(keystore, "Failed to validate keys: %s", ++ strerror(-rc)); ++ } else { ++ pr_verbose(keystore, "Successfully validated keys"); ++ printf("%lu keys are valid, %lu keys are invalid, %lu " ++ "warnings\n", info.num_valid, info.num_invalid, ++ info.num_warnings); ++ } ++ return rc; ++} ++ ++struct reencipher_params { ++ bool from_old; ++ bool to_new; ++ bool complete; ++ int inplace; /* -1 = autodetect, 0 = not in-place, 1 = in-place */ ++}; ++ ++struct reencipher_info { ++ struct reencipher_params params; ++ int pkey_fd; ++ t_CSNBKTC dll_CSNBKTC; ++ unsigned long num_reenciphered; ++ unsigned long num_failed; ++ unsigned long num_skipped; ++}; ++ ++/** ++ * Perform the reencipherment of a key ++ * ++ * @param[in] keystore the keystore ++ * @param[in] name the name of the key ++ * @param[in] dll_CSNBKTC the CCA key token change function ++ * @param[in] params reenciphering parameters ++ * @param[in] secure_key a buffer containing the secure key ++ * @param[in] secure_key_size the size of the secure key ++ * @param[in] is_old_mk if true the key is currently re-enciphered with the ++ * OLD master key ++ * @returns 0 if the re-enciphering is successful, a negative errno value ++ * otherwise, 1 if it was skipped ++ */ ++static int _keystore_perform_reencipher(struct keystore *keystore, ++ const char *name, ++ t_CSNBKTC dll_CSNBKTC, ++ struct reencipher_params *params, ++ u8 *secure_key, size_t secure_key_size, ++ bool is_old_mk) ++{ ++ int rc; ++ ++ if (!params->from_old && !params->to_new) { ++ /* Autodetect reencipher mode */ ++ if (is_old_mk) { ++ params->from_old = 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 { ++ params->to_new = 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); ++ } ++ } ++ ++ if (params->from_old) { ++ if (!is_old_mk) { ++ printf("The secure key '%s' is already enciphered " ++ "with the CURRENT CCA master key\n", name); ++ return 1; ++ } ++ ++ if (params->inplace == -1) ++ params->inplace = 1; ++ ++ pr_verbose(keystore, ++ "Secure key '%s' will be re-enciphered from OLD " ++ "to the CURRENT CCA master key", name); ++ ++ rc = key_token_change(dll_CSNBKTC, ++ secure_key, secure_key_size, ++ METHOD_OLD_TO_CURRENT, ++ keystore->verbose); ++ if (rc != 0) { ++ warnx("Failed to re-encipher '%s' from OLD to " ++ "CURRENT CCA master key", name); ++ return rc; ++ } ++ } ++ if (params->to_new) { ++ pr_verbose(keystore, ++ "Secure key '%s' will be re-enciphered from " ++ "CURRENT to the NEW CCA master key", name); ++ ++ if (params->inplace == -1) ++ params->inplace = 0; ++ ++ rc = key_token_change(dll_CSNBKTC, ++ secure_key, secure_key_size, ++ METHOD_CURRENT_TO_NEW, ++ keystore->verbose); ++ if (rc != 0) { ++ warnx("Failed to re-encipher '%s' from CURRENT to " ++ "NEW CCA master key", name); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * Processing function for the key re-enciphering function. ++ * ++ * @param[in] keystore the keystore ++ * @param[in] name the name of the key ++ * @param[in] properties the properties object of the key (not used here) ++ * @param[in] file_names the file names used by this key ++ * @param[in] private private data: struct reencipher_info ++ * ++ * @returns 0 if the re-enciphering is successful, a negative errno value ++ * otherwise ++ */ ++static int _keystore_process_reencipher(struct keystore *keystore, ++ const char *name, ++ struct properties *properties, ++ struct key_filenames *file_names, ++ void *private) ++{ ++ struct reencipher_info *info = (struct reencipher_info *)private; ++ struct reencipher_params params = info->params; ++ size_t clear_key_bitsize; ++ size_t secure_key_size; ++ u8 *secure_key = NULL; ++ char *out_file; ++ int is_old_mk; ++ char *temp; ++ int rc; ++ ++ rc = _keystore_ensure_keyfiles_exist(file_names, name); ++ if (rc != 0) ++ goto out; ++ ++ pr_verbose(keystore, "Complete reencipher: %d", params.complete); ++ pr_verbose(keystore, "In-place reencipher: %d", params.inplace); ++ ++ if (params.complete) { ++ if (!_keystore_reencipher_key_exists(file_names)) { ++ warnx("Staged re-enciphering in not pending for key " ++ "'%s', skipping", ++ name); ++ info->num_skipped++; ++ rc = 0; ++ goto out; ++ } ++ ++ printf("Completing re-enciphering for key '%s'\n", name); ++ ++ params.inplace = 1; ++ } ++ ++ secure_key = read_secure_key(params.complete ? ++ file_names->renc_filename : ++ file_names->skey_filename, ++ &secure_key_size, keystore->verbose); ++ if (secure_key == NULL) { ++ rc = -ENOENT; ++ goto out; ++ } ++ ++ rc = validate_secure_key(info->pkey_fd, secure_key, secure_key_size, ++ &clear_key_bitsize, &is_old_mk, ++ keystore->verbose); ++ if (rc != 0) { ++ if (params.complete) { ++ warnx("Key '%s' is not valid, re-enciphering is not " ++ "completed", name); ++ warnx("Possibly the CCA master key not yet been set?"); ++ } else { ++ warnx("Key '%s' is not valid, it is not re-enciphered", ++ name); ++ info->num_skipped++; ++ rc = 0; ++ } ++ goto out; ++ } ++ ++ if (!params.complete) { ++ printf("Re-enciphering key '%s'\n", name); ++ ++ rc = _keystore_perform_reencipher(keystore, name, ++ info->dll_CSNBKTC, ¶ms, ++ secure_key, secure_key_size, ++ is_old_mk); ++ if (rc < 0) ++ goto out; ++ if (rc > 0) { ++ info->num_skipped++; ++ rc = 0; ++ goto out; ++ } ++ } ++ ++ pr_verbose(keystore, "In-place reencipher: %d", params.inplace); ++ ++ out_file = params.inplace == 1 ? file_names->skey_filename : ++ file_names->renc_filename; ++ rc = write_secure_key(out_file, secure_key, ++ secure_key_size, keystore->verbose); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_set_file_permission(keystore, out_file); ++ if (rc != 0) ++ goto out; ++ ++ if (params.complete || params.inplace == 1) { ++ rc = _keystore_set_timestamp_property(properties, ++ PROP_NAME_REENC_TIME); ++ if (rc != 0) ++ goto out; ++ ++ rc = properties_save(properties, file_names->info_filename, 1); ++ if (rc != 0) { ++ pr_verbose(keystore, ++ "Failed to write key info file '%s': %s", ++ file_names->info_filename, strerror(-rc)); ++ goto out; ++ } ++ ++ rc = _keystore_set_file_permission(keystore, ++ file_names->info_filename); ++ if (rc != 0) ++ goto out; ++ } ++ ++ if (params.complete || ++ (params.inplace && _keystore_reencipher_key_exists(file_names))) { ++ if (remove(file_names->renc_filename) != 0) { ++ rc = -errno; ++ pr_verbose(keystore, "Failed to remove '%s': %s", ++ file_names->renc_filename, strerror(-rc)); ++ goto out; ++ } ++ } ++ ++ if (params.inplace != 1) { ++ util_asprintf(&temp, "Staged re-enciphering has completed for " ++ "key '%s'. Run 'zkey reencipher' with option " ++ "'--complete' when the NEW CCA master key has " ++ "been set (moved to the CURRENT master key " ++ "register) to complete the re-enciphering " ++ "process", name); ++ util_print_indented(temp, 0); ++ free(temp); ++ } ++ ++ info->num_reenciphered++; ++ ++out: ++ if (secure_key != NULL) ++ free(secure_key); ++ ++ printf("\n"); ++ ++ if (rc != 0) { ++ info->num_failed++; ++ pr_verbose(keystore, "Failed to re-encipher key '%s': %s", ++ name, strerror(-rc)); ++ rc = 0; ++ } ++ return rc; ++} ++ ++/** ++ * Reenciphers a key in the keystore ++ * ++ * @param[in] keystore the key store ++ * @param[in] name_filter the name filter to select the key (can be NULL) ++ * @param[in] apqn_filter the APQN filter to seletc the key (can be NULL) ++ * @param[in] from_old If true the key is reenciphered from the OLD to the ++ * CURRENT CCA master key. ++ * @param[in] to_new If true the key is reenciphered from the CURRENT to ++ * the OLD CCA master key. ++ * @param[in] inplace if true, the key will be re-enciphere in-place ++ * @param[in] staged if true, the key will be re-enciphere not in-place ++ * @param[in] complete if true, a pending re-encipherment is completed ++ * Note: if both from Old and toNew are FALSE, then the reencipherement mode is ++ * detected automatically. If both are TRUE then the key is reenciphered ++ * from the OLD to the NEW CCA master key. ++ * Note: if both inplace and staged are FLASE, then the key is re-enciphered ++ * inplace when for OLD-to-CURRENT, and is reenciphered staged for ++ * CURRENT-to-NEW. ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, ++ const char *apqn_filter, ++ bool from_old, bool to_new, bool inplace, ++ bool staged, bool complete, int pkey_fd, ++ t_CSNBKTC dll_CSNBKTC) ++{ ++ struct reencipher_info info; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ ++ info.params.from_old = from_old; ++ info.params.to_new = to_new; ++ info.params.inplace = -1; ++ if (inplace) ++ info.params.inplace = 1; ++ if (staged) ++ info.params.inplace = 0; ++ info.params.complete = complete; ++ info.pkey_fd = pkey_fd; ++ info.dll_CSNBKTC = dll_CSNBKTC; ++ info.num_failed = 0; ++ info.num_reenciphered = 0; ++ info.num_skipped = 0; ++ ++ rc = _keystore_process_filtered(keystore, name_filter, NULL, ++ apqn_filter, ++ _keystore_process_reencipher, &info); ++ ++ if (rc != 0) { ++ pr_verbose(keystore, "Failed to re-encipher keys: %s", ++ strerror(-rc)); ++ } else { ++ pr_verbose(keystore, "Successfully re-enciphered keys"); ++ printf("%lu keys re-enciphered, %lu keys skipped, %lu keys " ++ "failed to re-encipher\n", ++ info.num_reenciphered, info.num_skipped, ++ info.num_failed); ++ if (info.num_failed > 0) ++ rc = -EIO; ++ } ++ return rc; ++} ++ ++/** ++ * Copies (duplicates) a key in the keystore. Any existing volume associations ++ * are removed from the copy, because a volume can only be associated to one ++ * key. However, you can set new volume associations using the volumes ++ * parameter. ++ * ++ * @param[in] keystore the key store ++ * @param[in] name the name of the key ++ * @param[in] newname the new name of the key ++ * @param[in] volumes a comma separated list of volumes associated with this ++ * key (optional, can be NULL) ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int keystore_copy_key(struct keystore *keystore, const char *name, ++ const char *newname, const char *volumes) ++{ ++ struct volume_check vol_check = { .keystore = keystore, ++ .name = newname }; ++ struct key_filenames file_names = { NULL, NULL, NULL }; ++ struct key_filenames new_names = { NULL, NULL, NULL }; ++ struct properties *key_prop = NULL; ++ size_t secure_key_size; ++ u8 *secure_key; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ util_assert(name != NULL, "Internal error: name is NULL"); ++ util_assert(newname != NULL, "Internal error: newname is NULL"); ++ ++ rc = _keystore_get_key_filenames(keystore, name, &file_names); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_ensure_keyfiles_exist(&file_names, name); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_get_key_filenames(keystore, newname, &new_names); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_ensure_keyfiles_not_exist(&new_names, newname); ++ if (rc != 0) ++ goto out; ++ ++ secure_key = read_secure_key(file_names.skey_filename, ++ &secure_key_size, keystore->verbose); ++ if (secure_key == NULL) { ++ rc = -ENOENT; ++ goto out; ++ } ++ ++ rc = write_secure_key(new_names.skey_filename, secure_key, ++ secure_key_size, keystore->verbose); ++ free(secure_key); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_set_file_permission(keystore, new_names.skey_filename); ++ if (rc != 0) ++ goto out; ++ ++ key_prop = properties_new(); ++ rc = properties_load(key_prop, file_names.info_filename, 1); ++ if (rc != 0) { ++ warnx("Key '%s' does not exist or is invalid", name); ++ remove(file_names.skey_filename); ++ goto out; ++ } ++ ++ /* ++ * Remove any volume association, since a volume can only be associated ++ * with one key ++ */ ++ rc = properties_set(key_prop, PROP_NAME_VOLUMES, ""); ++ if (rc != 0) ++ goto out; ++ ++ if (volumes != NULL) { ++ rc = _keystore_change_association(key_prop, PROP_NAME_VOLUMES, ++ volumes, ++ "volume", ++ _keystore_volume_check, ++ &vol_check); ++ if (rc != 0) ++ goto out; ++ } ++ ++ rc = properties_remove(key_prop, PROP_NAME_CHANGE_TIME); ++ if (rc != 0 && rc != -ENOENT) ++ goto out; ++ ++ rc = properties_remove(key_prop, PROP_NAME_REENC_TIME); ++ if (rc != 0 && rc != -ENOENT) ++ goto out; ++ ++ rc = _keystore_set_timestamp_property(key_prop, ++ PROP_NAME_CREATION_TIME); ++ if (rc != 0) ++ goto out; ++ ++ rc = properties_save(key_prop, new_names.info_filename, 1); ++ if (rc != 0) { ++ pr_verbose(keystore, ++ "Key info file '%s' could not be written: %s", ++ new_names.info_filename, strerror(-rc)); ++ remove(new_names.skey_filename); ++ goto out; ++ } ++ ++ rc = _keystore_set_file_permission(keystore, new_names.info_filename); ++ if (rc != 0) ++ goto out; ++ ++ pr_verbose(keystore, "Successfully copied key '%s' to '%s'", name, ++ newname); ++ ++out: ++ if (rc != 0) { ++ remove(new_names.skey_filename); ++ remove(new_names.info_filename); ++ } ++ ++ _keystore_free_key_filenames(&file_names); ++ _keystore_free_key_filenames(&new_names); ++ if (key_prop != NULL) ++ properties_free(key_prop); ++ ++ if (rc != 0) ++ pr_verbose(keystore, "Failed to copy key '%s'to '%s': %s", ++ name, newname, strerror(-rc)); ++ return rc; ++} ++ ++/** ++ * Exports a key from the keystore to a file ++ * ++ * @param[in] keystore the key store ++ * @param[in] name the name of the key ++ * @param[in] export_file the name of the file to export the key to ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int keystore_export_key(struct keystore *keystore, const char *name, ++ const char *export_file) ++{ ++ struct key_filenames file_names = { NULL, NULL, NULL }; ++ size_t secure_key_size; ++ u8 *secure_key; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ util_assert(name != NULL, "Internal error: name is NULL"); ++ util_assert(export_file != NULL, "Internal error: export_file is NULL"); ++ ++ rc = _keystore_get_key_filenames(keystore, name, &file_names); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_ensure_keyfiles_exist(&file_names, name); ++ if (rc != 0) ++ goto out; ++ ++ secure_key = read_secure_key(file_names.skey_filename, ++ &secure_key_size, keystore->verbose); ++ if (secure_key == NULL) { ++ rc = -ENOENT; ++ goto out; ++ } ++ ++ rc = write_secure_key(export_file, secure_key, ++ secure_key_size, keystore->verbose); ++ free(secure_key); ++ ++ pr_verbose(keystore, "Successfully exported key '%s' to '%s'", name, ++ export_file); ++ ++out: ++ _keystore_free_key_filenames(&file_names); ++ ++ if (rc != 0) ++ pr_verbose(keystore, "Failed to export key '%s': %s", ++ name, strerror(-rc)); ++ return rc; ++} ++ ++/** ++ * Prompts the user to confirm deletion of a key ++ * ++ * @param[in] keystore the key store ++ * @param[in] name the name of the key ++ * @param[in] file_names the file names of the key ++ * ++ * @returnd 0 if the user confirmed the deletion, a negative errno value ++ * otherwise ++ */ ++static int _keystore_propmp_for_remove(struct keystore *keystore, ++ const char *name, ++ struct key_filenames *file_names) ++{ ++ struct properties *key_prop; ++ char *volumes = NULL; ++ char **volume_list = NULL; ++ char str[20]; ++ int rc, i; ++ ++ key_prop = properties_new(); ++ rc = properties_load(key_prop, file_names->info_filename, 1); ++ if (rc != 0) { ++ warnx("Key '%s' does not exist or is invalid", name); ++ goto out; ++ } ++ ++ volumes = properties_get(key_prop, PROP_NAME_VOLUMES); ++ if (volumes != NULL && strlen(volumes) > 0) { ++ volume_list = str_list_split(volumes); ++ ++ warnx("When you remove key '%s' the following volumes will " ++ "no longer be usable:", name); ++ for (i = 0; volume_list[i] != NULL; i++) ++ fprintf(stderr, "%s\n", volume_list[i]); ++ } ++ ++ printf("%s: Remove key '%s'? ", program_invocation_short_name, name); ++ if (fgets(str, sizeof(str), stdin) == NULL) { ++ rc = -EIO; ++ goto out; ++ } ++ if (str[strlen(str) - 1] == '\n') ++ str[strlen(str) - 1] = '\0'; ++ pr_verbose(keystore, "Prompt reply: '%s'", str); ++ if (strcasecmp(str, "y") != 0 && strcasecmp(str, "yes") != 0) { ++ rc = -ECANCELED; ++ goto out; ++ } ++ ++out: ++ properties_free(key_prop); ++ if (volume_list != NULL) ++ str_list_free_string_array(volume_list); ++ ++ return rc; ++} ++ ++/** ++ * Removes a key from the keystore ++ * ++ * @param[in] keystore the key store ++ * @param[in] name the name of the key ++ * @param[in] quiet if true no confirmation prompt is shown ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int keystore_remove_key(struct keystore *keystore, const char *name, ++ bool quiet) ++{ ++ struct key_filenames file_names = { NULL, NULL, NULL }; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ util_assert(name != NULL, "Internal error: name is NULL"); ++ ++ rc = _keystore_get_key_filenames(keystore, name, &file_names); ++ if (rc != 0) ++ goto out; ++ ++ rc = _keystore_ensure_keyfiles_exist(&file_names, name); ++ if (rc != 0) ++ goto out; ++ ++ if (!quiet) { ++ if (_keystore_propmp_for_remove(keystore, name, ++ &file_names) != 0) ++ goto out; ++ } ++ ++ if (remove(file_names.skey_filename) != 0) { ++ rc = -errno; ++ pr_verbose(keystore, "Failed to remove '%s': %s", ++ file_names.skey_filename, strerror(-rc)); ++ goto out; ++ } ++ if (remove(file_names.info_filename) != 0) { ++ rc = -errno; ++ pr_verbose(keystore, "Failed to remove '%s': %s", ++ file_names.info_filename, strerror(-rc)); ++ } ++ if (_keystore_reencipher_key_exists(&file_names)) { ++ if (remove(file_names.renc_filename) != 0) { ++ rc = -errno; ++ pr_verbose(keystore, "Failed to remove '%s': %s", ++ file_names.renc_filename, strerror(-rc)); ++ } ++ } ++ pr_verbose(keystore, "Successfully removed key '%s'", name); ++ ++out: ++ _keystore_free_key_filenames(&file_names); ++ ++ if (rc != 0) ++ pr_verbose(keystore, "Failed to remove key '%s': %s", ++ name, strerror(-rc)); ++ return rc; ++} ++ ++/** ++ * Processing function for the key display function. ++ * ++ * @param[in] keystore the keystore ++ * @param[in] name the name of the key ++ * @param[in] properties the properties object of the key ++ * @param[in] file_names the file names used by this key ++ * @param[in] private private data: struct reencipher_info ++ * ++ * @returns 0 if the display is successful, a negative errno value otherwise ++ */ ++static int _keystore_display_key(struct keystore *keystore, ++ const char *name, ++ struct properties *properties, ++ struct key_filenames *file_names, ++ void *private) ++{ ++ struct util_rec *rec = (struct util_rec *)private; ++ struct secaeskeytoken *secure_key; ++ size_t secure_key_size; ++ int rc = 0; ++ ++ secure_key = (struct secaeskeytoken *) ++ read_secure_key(file_names->skey_filename, ++ &secure_key_size, keystore->verbose); ++ if (secure_key == NULL) ++ return -EIO; ++ ++ if (secure_key_size < SECURE_KEY_SIZE) { ++ pr_verbose(keystore, ++ "Size of secure key is too small: %lu expected %lu", ++ secure_key_size, SECURE_KEY_SIZE); ++ rc = -EIO; ++ goto out; ++ } ++ ++ _keystore_print_record(rec, name, properties, 0, ++ file_names->skey_filename, secure_key_size, ++ IS_XTS(secure_key_size) ? secure_key->bitsize * 2 ++ : secure_key->bitsize, ++ 0, 0, ++ _keystore_reencipher_key_exists(file_names)); ++ ++out: ++ free(secure_key); ++ return rc; ++} ++ ++/** ++ * Lists keys in the keystore that matches the filters ++ * ++ * @param[in] keystore the key store ++ * @param[in] name_filter the name filter. Can contain wild cards. ++ * NULL means no name filter. ++ * @param[in] volume_filter the volume filter. Can contain wild cards, and ++ * mutliple volume filters separated by commas. ++ * The ':dm-name' part of the volume is optional ++ * for the volume filter. If not specified, the filter ++ * checks the volume part only. ++ * NULL means no volume filter. ++ * @param[in] apqn_filter the APQN filter. Can contain wild cards, and ++ * mutliple APQN filters separated by commas. ++ * NULL means no APQN filter. ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int keystore_list_keys(struct keystore *keystore, const char *name_filter, ++ const char *volume_filter, const char *apqn_filter) ++{ ++ struct util_rec *rec; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ ++ rec = _keystore_setup_record(0); ++ ++ rc = _keystore_process_filtered(keystore, name_filter, volume_filter, ++ apqn_filter, _keystore_display_key, ++ rec); ++ util_rec_free(rec); ++ ++ if (rc != 0) ++ pr_verbose(keystore, "Failed to list keys: %s", ++ strerror(-rc)); ++ else ++ pr_verbose(keystore, "Successfully listed keys"); ++ return rc; ++} ++ ++/** ++ * Executes a command via system(). ++ * ++ * @param[in] cmd the command to execute ++ * @param[in] msg_cmd the short command name (for messages) ++ * ++ * @returns the exit code of the command execution, or -1 in case of an error ++ */ ++static int _keystore_execute_cmd(const char *cmd, ++ const char *msg_cmd) ++{ ++ int rc; ++ ++ rc = setenv("PATH", "/bin:/usr/bin:/usr/sbin", 1); ++ if (rc < 0) ++ return rc; ++ ++ rc = system(cmd); ++ if (WIFEXITED(rc)) { ++ rc = WEXITSTATUS(rc); ++ if (rc != 0) ++ printf("%s exit code: %d\n", msg_cmd, rc); ++ } else { ++ rc = -EIO; ++ warnx("%s terminated abnormally", msg_cmd); ++ } ++ ++ return rc; ++} ++ ++ ++struct crypt_info { ++ bool execute; ++ char **volume_filter; ++ int (*process_func)(struct keystore *keystore, ++ const char *volume, ++ const char *dmname, ++ const char *cipher_spec, ++ const char *key_file_name, ++ size_t key_file_size, ++ size_t sector_size, ++ struct crypt_info *info); ++}; ++ ++/** ++ * Processing function for the cryptsetup function. Builds a cryptsetup command ++ * line and optionally executes it. ++ * ++ * @param[in] keystore the keystore (not used here) ++ * @param[in] volume the volume to mount ++ * @param[in] dmname the debice mapper name ++ * @param[in] cipher_spec the cipher specification ++ * @param[in] key_file_name the key file name ++ * @param[in] key_file_size the size of the key file in bytes ++ * @param sector_size the sector size in bytes or 0 if not specified ++ * @param[in] info processing info ++ * ++ * @returns 0 if successful, a negative errno value otherwise ++ */ ++static int _keystore_process_cryptsetup(struct keystore *keystore, ++ const char *volume, ++ const char *dmname, ++ const char *cipher_spec, ++ const char *key_file_name, ++ size_t key_file_size, ++ size_t sector_size, ++ struct crypt_info *info) ++{ ++ char temp[100]; ++ int rc = 0; ++ char *cmd; ++ ++ sprintf(temp, "--sector-size %lu ", sector_size); ++ util_asprintf(&cmd, ++ "cryptsetup plainOpen %s--key-file '%s' --key-size %lu " ++ "--cipher %s %s%s %s", ++ keystore->verbose ? "-v " : "", key_file_name, ++ key_file_size * 8, cipher_spec, ++ sector_size > 0 ? temp : "", volume, dmname); ++ ++ if (info->execute) { ++ printf("Executing: %s\n", cmd); ++ rc = _keystore_execute_cmd(cmd, "cryptsetup"); ++ } else { ++ printf("%s\n", cmd); ++ } ++ ++ free(cmd); ++ return rc; ++} ++ ++/** ++ * Processing function for the crypttab function. Builds a crypttab entry ++ * and prints it. ++ * ++ * @param[in] keystore the keystore (not used here) ++ * @param[in] volume the volume to mount ++ * @param[in] dmname the debice mapper name ++ * @param[in] cipher_spec the cipher specification ++ * @param[in] key_file_name the key file name ++ * @param[in] key_file_size the size of the key file in bytes ++ * @param sector_size the sector size in bytes or 0 if not specified ++ * @param[in] info processing info (not used here) ++ * ++ * @returns 0 if successful, a negative errno value otherwise ++ */ ++ ++static int _keystore_process_crypttab(struct keystore *UNUSED(keystore), ++ const char *volume, ++ const char *dmname, ++ const char *cipher_spec, ++ const char *key_file_name, ++ size_t key_file_size, ++ size_t sector_size, ++ struct crypt_info *UNUSED(info)) ++{ ++ char temp[1000]; ++ ++ if (sector_size > 0) { ++ sprintf(temp, ++ "WARNING: volume '%s' is using a sector size of %lu. " ++ "At the time this utility was developed, systemd's " ++ "support of crypttab did not support to specify a " ++ "sector size with plain dm-crypt devices. The generated " ++ "crypttab entry may or may not work, and may need " ++ "manual adoptions.", volume, sector_size); ++ util_print_indented(temp, 0); ++ } ++ ++ sprintf(temp, ",sector-size=%lu", sector_size); ++ printf("%s\t%s\t%s\tplain,cipher=%s,size=%lu,hash=plain%s\n", ++ dmname, volume, key_file_name, cipher_spec, key_file_size * 8, ++ sector_size > 0 ? temp : ""); ++ ++ return 0; ++} ++ ++/** ++ * Builds a cipher specification for cryptsetup/crypttab ++ * ++ * @param properties the key properties ++ * @param is_xts if true, the key is an XTS key ++ * ++ * @returns the cipher spec string (must be freed by the caller) ++ */ ++static char *_keystore_build_cipher_spec(struct properties *properties, ++ bool is_xts) ++{ ++ char *cipher_spec = NULL; ++ char *cipher = NULL; ++ char *ivmode = NULL; ++ ++ cipher = properties_get(properties, PROP_NAME_CIPHER); ++ if (cipher == NULL) ++ goto out; ++ ++ ivmode = properties_get(properties, PROP_NAME_IV_MODE); ++ if (ivmode == NULL) ++ goto out; ++ ++ util_asprintf(&cipher_spec, "%s-%s-%s", cipher, is_xts ? "xts" : "cbc", ++ ivmode); ++ ++out: ++ if (cipher != NULL) ++ free(cipher); ++ if (ivmode != NULL) ++ free(ivmode); ++ ++ return cipher_spec; ++} ++ ++/** ++ * Returns the size of the secure key file ++ * ++ * @param[in] keystore the keystore ++ * @param[in] skey_filename the file name of the secure key ++ * ++ * @returns the size of the secure key, or -1 in case of an error ++ */ ++static size_t _keystore_get_key_file_size(struct keystore *keystore, ++ const char *skey_filename) ++{ ++ size_t secure_key_size; ++ struct stat sb; ++ ++ if (stat(skey_filename, &sb)) { ++ pr_verbose(keystore, "Key file '%s': %s", ++ skey_filename, strerror(errno)); ++ return -1; ++ } ++ ++ secure_key_size = sb.st_size; ++ if (secure_key_size < SECURE_KEY_SIZE) { ++ pr_verbose(keystore, ++ "Size of secure key is too small: %lu expected %lu", ++ secure_key_size, SECURE_KEY_SIZE); ++ return -1; ++ } ++ ++ return secure_key_size; ++} ++ ++/** ++ * Processing function for the cryptsetup and crypttab functions. ++ * Extracts the required information and calls the secondary processing function ++ * contained in struct crypt_info. ++ * ++ * @param[in] keystore the keystore ++ * @param[in] name the name of the key ++ * @param[in] properties the properties object of the key ++ * @param[in] file_names the file names used by this key ++ * @param[in] private private data: struct crypt_info ++ * ++ * @returns 0 if the validation is successful, a negative errno value otherwise ++ */ ++static int _keystore_process_crypt(struct keystore *keystore, ++ const char *name, ++ struct properties *properties, ++ struct key_filenames *file_names, ++ void *private) ++{ ++ struct crypt_info *info = (struct crypt_info *)private; ++ char **volume_list = NULL; ++ char *cipher_spec = NULL; ++ size_t secure_key_size; ++ size_t sector_size = 0; ++ char *volumes = NULL; ++ char *dmname; ++ char *temp; ++ int rc = 0; ++ char *vol; ++ char *ch; ++ int i; ++ ++ secure_key_size = _keystore_get_key_file_size(keystore, ++ file_names->skey_filename); ++ if (secure_key_size < SECURE_KEY_SIZE) { ++ pr_verbose(keystore, ++ "Size of secure key is too small: %lu expected %lu", ++ secure_key_size, SECURE_KEY_SIZE); ++ rc = -EIO; ++ goto out; ++ } ++ ++ cipher_spec = _keystore_build_cipher_spec(properties, ++ IS_XTS(secure_key_size)); ++ if (cipher_spec == NULL) { ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ volumes = properties_get(properties, PROP_NAME_VOLUMES); ++ if (volumes == NULL) ++ return -EINVAL; ++ volume_list = str_list_split(volumes); ++ ++ temp = properties_get(properties, PROP_NAME_SECTOR_SIZE); ++ if (temp != NULL) { ++ util_assert(sscanf(temp, "%lu", §or_size) == 1, ++ "Internal error: sscanf failed"); ++ free(temp); ++ } ++ ++ for (i = 0; volume_list[i] != NULL && rc == 0; i++) { ++ vol = volume_list[i]; ++ if (_keystore_match_filter(vol, info->volume_filter, ++ NULL) != 0) { ++ ch = strchr(vol, ':'); ++ if (ch == NULL) { ++ warnx("Volume does not contain a dm-name part." ++ " Key: '%s'", name); ++ rc = -EINVAL; ++ break; ++ } ++ *ch = '\0'; ++ dmname = ch + 1; ++ ++ rc = info->process_func(keystore, vol, dmname, ++ cipher_spec, file_names->skey_filename, ++ secure_key_size, sector_size, info); ++ if (rc != 0) ++ break; ++ } ++ } ++ ++out: ++ if (volumes != NULL) ++ free(volumes); ++ if (volume_list != NULL) ++ str_list_free_string_array(volume_list); ++ if (cipher_spec != NULL) ++ free(cipher_spec); ++ return rc; ++} ++ ++/** ++ * Generates cryptsetup commands for one or multiple volumes. ++ * ++ * @param[in] keystore the key store ++ * @param[in] volume_filter the volume filter. Can contain wild cards, and ++ * mutliple volume filters separated by commas. ++ * The ':dm-name' part of the volume is optional ++ * for the volume filter. If not specified, the filter ++ * checks the volume part only. ++ * @param[in] execute If TRUE the cryptsetup command is executed, ++ * otherwise it is printed to stdout ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, ++ bool execute) ++{ ++ struct crypt_info info = { 0 }; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ ++ if (volume_filter == NULL) ++ volume_filter = "*"; ++ info.execute = execute; ++ info.volume_filter = str_list_split(volume_filter); ++ info.process_func = _keystore_process_cryptsetup; ++ ++ rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL, ++ _keystore_process_crypt, &info); ++ ++ str_list_free_string_array(info.volume_filter); ++ ++ if (rc < 0) ++ pr_verbose(keystore, "Cryptsetup failed with: %s", ++ strerror(-rc)); ++ else if (rc > 0) ++ pr_verbose(keystore, "Cryptsetup failed with: %d", rc); ++ else ++ pr_verbose(keystore, ++ "Successfully generated cryptsetup commands"); ++ ++ return rc; ++} ++ ++/** ++ * Generates crypttab entries for one or multiple volumes. ++ * ++ * @param[in] keystore the key store ++ * @param[in] volume_filter the volume filter. Can contain wild cards, and ++ * mutliple volume filters separated by commas. ++ * The ':dm-name' part of the volume is optional ++ * for the volume filter. If not specified, the filter ++ * checks the volume part only. ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++int keystore_crypttab(struct keystore *keystore, const char *volume_filter) ++{ ++ struct crypt_info info = { 0 }; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ ++ if (volume_filter == NULL) ++ volume_filter = "*"; ++ info.volume_filter = str_list_split(volume_filter); ++ info.process_func = _keystore_process_crypttab; ++ ++ rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL, ++ _keystore_process_crypt, &info); ++ ++ str_list_free_string_array(info.volume_filter); ++ ++ if (rc != 0) ++ pr_verbose(keystore, "Cryptsetup failed with: %s", ++ strerror(-rc)); ++ else ++ pr_verbose(keystore, "Successfully generated crypttab entries"); ++ ++ return rc; ++} ++ ++/** ++ * Frees a keystore object ++ * ++ * @param[in] keystore the key store ++ */ ++void keystore_free(struct keystore *keystore) ++{ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ ++ _keystore_unlock_repository(keystore); ++ free(keystore->directory); ++ free(keystore); ++} +--- /dev/null ++++ b/zkey/keystore.h +@@ -0,0 +1,77 @@ ++/* ++ * zkey - Generate, re-encipher, and validate secure keys ++ * ++ * Keystore handling functions ++ * ++ * Copyright IBM Corp. 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. ++ */ ++ ++#ifndef KEYSTORE_H ++#define KEYSTORE_H ++ ++#include ++ ++#include "pkey.h" ++ ++struct keystore { ++ bool verbose; ++ char *directory; ++ int lock_fd; ++ mode_t mode; ++ gid_t owner; ++}; ++ ++struct keystore *keystore_new(const char *directory, bool verbose); ++ ++int keystore_generate_key(struct keystore *keystore, const char *name, ++ const char *description, const char *volumes, ++ const char *apqns, size_t sector_size, ++ size_t keybits, bool xts, const char *clear_key_file, ++ int pkey_fd); ++ ++int keystore_import_key(struct keystore *keystore, const char *name, ++ const char *description, const char *volumes, ++ const char *apqns, size_t sector_size, ++ const char *import_file); ++ ++int keystore_change_key(struct keystore *keystore, const char *name, ++ const char *description, const char *volumes, ++ const char *apqns, long int sector_size); ++ ++int keystore_rename_key(struct keystore *keystore, const char *name, ++ const char *newname); ++ ++int keystore_validate_key(struct keystore *keystore, const char *name_filter, ++ const char *apqn_filter, int pkey_fd); ++ ++int keystore_reencipher_key(struct keystore *keystore, const char *name_filter, ++ const char *apqn_filter, ++ bool from_old, bool to_new, bool inplace, ++ bool staged, bool complete, int pkey_fd, ++ t_CSNBKTC dll_CSNBKTC); ++ ++int keystore_copy_key(struct keystore *keystore, const char *name, ++ const char *newname, const char *volumes); ++ ++int keystore_export_key(struct keystore *keystore, const char *name, ++ const char *export_file); ++ ++int keystore_remove_key(struct keystore *keystore, const char *name, ++ bool quiet); ++ ++int keystore_list_keys(struct keystore *keystore, const char *name_filter, ++ const char *volume_filter, const char *apqn_filter); ++ ++int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, ++ bool execute); ++ ++int keystore_crypttab(struct keystore *keystore, const char *volume_filter); ++ ++void keystore_free(struct keystore *keystore); ++ ++ ++ ++#endif diff --git a/s390-tools-sles15sp1-0006-zkey-Add-keystore-related-commands.patch b/s390-tools-sles15sp1-0006-zkey-Add-keystore-related-commands.patch new file mode 100644 index 0000000..f20bf5a --- /dev/null +++ b/s390-tools-sles15sp1-0006-zkey-Add-keystore-related-commands.patch @@ -0,0 +1,1418 @@ +Subject: zkey: Add keystore related commands +From: Philipp Rudo + +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 + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Philipp Rudo +--- + 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; + } diff --git a/s390-tools-sles15sp1-0007-zkey-Create-key-repository-and-group-during-make-ins.patch b/s390-tools-sles15sp1-0007-zkey-Create-key-repository-and-group-during-make-ins.patch new file mode 100644 index 0000000..9976f11 --- /dev/null +++ b/s390-tools-sles15sp1-0007-zkey-Create-key-repository-and-group-during-make-ins.patch @@ -0,0 +1,40 @@ +Subject: zkey: Create key repository and group during make install +From: Philipp Rudo + +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: 6a2f4fd3760420e11b23db13f8b736f87764d409 +Problem-ID: SEC1800 + +Upstream-Description: + + zkey: Create key repository and group during make install + + Create the default keystore directory '/etc/zkey/repository' + and the user group 'zkeyadm' during make install. + + Signed-off-by: Ingo Franzki + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Philipp Rudo +--- + zkey/Makefile | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -36,6 +36,9 @@ install: all + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zkey $(DESTDIR)$(USRBINDIR) + $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1 + $(INSTALL) -m 644 -c zkey.1 $(DESTDIR)$(MANDIR)/man1 ++ getent group zkeyadm >/dev/null || groupadd -r zkeyadm ++ $(INSTALL) -d -g zkeyadm -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey ++ $(INSTALL) -d -g zkeyadm -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey/repository + + endif + diff --git a/s390-tools-sles15sp1-0008-zkey-Man-page-updates.patch b/s390-tools-sles15sp1-0008-zkey-Man-page-updates.patch new file mode 100644 index 0000000..eabd889 --- /dev/null +++ b/s390-tools-sles15sp1-0008-zkey-Man-page-updates.patch @@ -0,0 +1,1023 @@ +Subject: zkey: Man page updates +From: Philipp Rudo + +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: f093d0bfd4242424515c6aa55c7b8ad94d233597 +Problem-ID: SEC1800 + +Upstream-Description: + + zkey: Man page updates + + Add documentation for the new keystore related zkey commands. + + Signed-off-by: Ingo Franzki + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Philipp Rudo +--- + zkey/zkey.1 | 870 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 811 insertions(+), 59 deletions(-) + +--- a/zkey/zkey.1 ++++ b/zkey/zkey.1 +@@ -1,38 +1,17 @@ +-.\" 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. + .\" +-.TH ZKEY 1 "February 2017" "s390-tools" ++.TH ZKEY 1 "May 2018" "s390-tools" + .SH NAME +-zkey \- Generates, re-enciphers, and validates secure AES keys ++zkey \- Manage secure AES keys + . + . + .SH SYNOPSIS + .B zkey +-.BR generate | gen +-.I secure\-key\-file +-.RB [ \-\-keybits | \-k +-.IB size ] +-.RB [ \-\-xts | \-x ] +-.RB [ \-\-clearkey | \-c +-.IB clear\-key\-file ] +-.RB [ \-\-verbose | \-V ] +-. +-.br +-.B zkey +-.BR validate | val +-.I secure\-key\-file +-.RB [ \-\-verbose | \-V ] +-. +-.br +-.B zkey +-.BR reencipher | re +-.I secure\-key\-file +-.RB [ \-\-to\-new | \-n ] +-.RB [ \-\-from\-old | \-o ] +-.RB [ \-\-output | \-f +-.IB output\-file ] +-.RB [ \-\-verbose | \-V ] ++.I command ++.RI [ secure\-key\-file ] ++.RB [ OPTIONS ] + . + .PP + .B zkey +@@ -44,64 +23,194 @@ zkey \- Generates, re-enciphers, and val + . + . + .SH DESCRIPTION +-Use the \fBzkey\fP command to generate secure AES keys that are enciphered +-with a master key of an IBM cryptographic adapter in CCA coprocessor mode. +-You can also use the \fBzkey\fP command to validate and re-encipher secure ++Use the \fBzkey\fP tool to generate and manage secure AES keys that are ++enciphered with a master key of an IBM cryptographic adapter in CCA coprocessor ++mode. You can also use the \fBzkey\fP tool to validate and re-encipher secure + AES keys. + .PP ++The secure keys can either be stored in a file in the file system, or ++in the secure key repository. The default location of the secure key repository ++is \fB/etc/zkey/repository\fP. Set environment variable \fBZKEY_REPOSITORY\fP ++to point to a different location to use a different secure key repository ++location. Keys stored in a secure key repository inherit the permissions from ++the repository directory (except write access for other users, which is always ++denied). The default repository location \fB/etc/zkey/repository\fP is created ++with group \fBzkeyadm\fP as owner and mode \fB770\fP. Thus all secure keys ++created in that repository are owned by group \fBzkeyadm\fP. Anyone that ++is supposed to access secure keys in the secure key repository must be part of ++group \fBzkeyadm\fP. ++.PP ++When storing the secure key in a key repository, additional information, such as ++a textual description of the key, can be associated with a secure key. ++You can associate a secure key with one or multiple cryptographic adapters ++(APQNs) that are set up with the same CCA master key. ++You can also associate a secure key with one or multiple volumes ++(block devices), which are encrypted using dm-crypt with the secure key. The ++volume association also contains the device-mapper name, separated by a colon, ++used with dm-crypt. A specific volume can only be associated with one secure ++key. ++.PP + The generated secure key is saved in a file with a size of 64 or 128 bytes. +-The file contains an AES key of 128, 192, or 256 bits enciphered with the +-master key of the CCA cryptographic adapter. ++The file contains an AES key with a length of 128, 192, or 256 bits. The key is ++enciphered with the master key of the CCA cryptographic adapter. + Secure keys that are used for the XTS cipher mode can be 128 or 256 bits +-in size, because XTS requires two concatenated secure keys. ++in size. ++. + . + . ++.SH COMMANDS ++The \fBzkey\fP tool can operate in two modes. When argument ++.I secure\-key\-file ++is specified then it operates on the secure key contained in the specified file. ++This applies to commands \fBgenerate\fP, \fBvalidate\fP, and \fBreencipher\fP. ++When the ++.B \-\-name ++option is specified then it operates on a secure key contained in the secure ++key repository. + . +-.SH USAGE ++.PP + .SS "Generating secure AES keys" ++. ++.B zkey ++.BR generate | gen ++.I secure\-key\-file ++.RB [ \-\-keybits | \-k ++.IB size ] ++.RB [ \-\-xts | \-x ] ++.RB [ \-\-clearkey | \-c ++.IB clear\-key\-file ] ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++.B zkey ++.BR generate | gen ++.B \-\-name | \-N ++.IB key-name ++.RB [ \-\-description | \-d ++.IB description ] ++.RB [ \-\-volumes | \-l ++.IB volume1:dmname1[,volume2:dmname2[,...]] ] ++.RB [ \-\-apqns | \-a ++.IB card1.domain1[,card2.domain2[,...]] ] ++.RB [ \-\-sector-size | \-S ++.IB bytes ] ++.RB [ \-\-keybits | \-k ++.IB size ] ++.RB [ \-\-xts | \-x ] ++.RB [ \-\-clearkey | \-c ++.IB clear\-key\-file ] ++.RB [ \-\-verbose | \-V ] ++.PP + Use the + .B generate + command to generate a new secure AES key either randomly within the CCA + cryptographic adapter, or from a clear AES key specified as input. When specifying +-a clear key as input, the clear key should be kept at a secure place, or be ++a clear key as input, the clear key should be kept in a secure place, or be + securely erased after creation of the secure key. The secure key itself does + not need to be kept secure, because it can only be used together with a + CCA cryptographic adapter that contains the master key with which the secure + key was generated. ++.PP ++The generated secure key can either be stored in a file in the file system, ++or in the secure key repository. To store the generated secure key in a ++file, specify the file name with option \fIsecure\-key\-file\fP. To store the ++secure key in the secure key repository, specify the name of the key using the ++.B --name ++option. When storing the secure key in a key repository, ++additional information can be associated with a secure key using the ++.B --description ++, ++.B --volumes ++, ++.B --apqns ++, or the ++.B --sector-size ++options. + . + .SS "Validating secure AES keys" ++. ++.B zkey ++.BR validate | val ++.I secure\-key\-file ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++.B zkey ++.BR validate | val ++.RB [ \-\-name | \-N ++.IB key-name ] ++.RB [ \-\-verbose | \-V ] ++.PP + Use the + .B validate + command to validate an existing secure key. +-It checks if the specified file contains a valid secure key. ++It checks if the specified file or repository entry contains a valid secure key. + It also displays the attributes of the secure key, such as key sizes, whether +-it is a secure key that can be used for the XTS cipher mode, and the master key +-register with which the secure key is enciphered. ++it is a secure key that can be used for the XTS cipher mode, the master key ++register (CURRENT or OLD) with which the secure key is enciphered, and other key ++attributes. For further information about master key registers, see the ++\fBreencipher\fP command. ++.PP ++The secure key can either be contained in a file in the file system, or in a ++secure key repository. To validate a secure key contained in a file, specify ++the file name with option \fIsecure\-key\-file\fP. To validate secure keys ++contained in the secure key repository, specify the name of the key ++or a pattern containing wildcards using the ++.B --name ++option. When wildcards are used you must quote the value. ++If neither option \fIsecure\-key\-file\fP nor option ++.B --name ++are specified, then all secure keys contained in the key repository ++are validated. + . + .SS "Re-encipher existing AES secure keys" ++. ++.B zkey ++.BR reencipher | re ++.I secure\-key\-file ++.RB [ \-\-to\-new | \-n ] ++.RB [ \-\-from\-old | \-o ] ++.RB [ \-\-output | \-f ++.IB output\-file ] ++.RB [ \-\-verbose | \-V ] ++.PP ++.B zkey ++.BR reencipher | re ++.RB [ \-\-name | \-N ++.IB key-name ] ++.RB [ \-\-apqns | \-a ++.IB card1.domain1[,card2.domain2[,...]] ] ++.RB [ \-\-to\-new | \-n ] ++.RB [ \-\-from\-old | \-o ] ++.RB [ \-\-in-place | \-i ] ++.RB [ \-\-staged | \-s ] ++.RB [ \-\-complete | \-c ] ++.RB [ \-\-verbose | \-V ] ++.PP + Use the + .B reencipher + command to re-encipher an existing secure key with a new master key. +-A secure key have to be re-enciphered, when the master key of the CCA +-cryptographic adapter is being changed. ++A secure key bust be re-enciphered when the master key of the CCA ++cryptographic adapter changes. + .PP +-The CCA cryptographic adapter has 3 different registers to store ++The CCA cryptographic adapter has three different registers to store + master keys: + .RS 2 + .IP "\(bu" 2 +-The \fPCURRENT\fP register contains the current master key. ++The \fBCURRENT\fP register contains the current master key. + . + .IP "\(bu" 2 + The \fBOLD\fP register contains the previously used master key. + Secure keys enciphered with the master key contained in the \fBOLD\fP +-register canstill be used until the master key is changed again. ++register can still be used until the master key is changed again. + . + .IP "\(bu" 2 + The \fBNEW\fP register contains the new master key to be set. + The master key in the \fBNEW\fP register cannot be used until it is made +-the current master key. Note that a secure key can be proactively +-re-enciphered with the master key in the \fBNEW\fP register before +-the new master key is made the current one. ++the current master key. You can pro-actively re-encipher a secure key with the ++\fBNEW\fP master key before this key is made the \fBCURRENT\fP key. Use the ++.B \-\-to-new ++option to do this. + .RE + .PP + Use the +@@ -110,13 +219,12 @@ option to re-encipher a secure key that + the master key in the \fBOLD\fP register with the master key in the + \fBCURRENT\fP register. + .PP +-Use the +-.B \-\-to\-new +-option to proactively re-encipher a secure key that is currently +-enciphered with the master key in the \fBCURRENT\fP register with +-the master key in the \fBNEW\fP register. + .PP +-If both options are specified, a secure key that is currently enciphered ++If both the ++.B \-\-from-old ++and ++.B \-\-to-new ++options are specified, a secure key that is currently enciphered + with the master key in the \fBOLD\fP register is re-enciphered with the + master key in the \fBNEW\fP register. + .PP +@@ -127,11 +235,310 @@ If currently enciphered with the master + it is re-enciphered with the master key in the \fBCURRENT\fP register. + If it is currently enciphered with the master key in the \fBCURRENT\fP + register, it is re-enciphered with the master key in the \fBNEW\fP register. ++If for this case the \fBNEW\fP register does not contain a valid master key, ++then the re-encipher operation fails. ++.PP ++The secure key can either be contained in a file in the file system, or in a ++secure key repository. To re-encipher a secure key contained in a file, ++specify the file name with option \fIsecure\-key\-file\fP. To re-encipher ++secure keys contained in the secure key repository, specify the name of the key ++or a pattern containing wildcards using the ++.B --name ++option. When wildcards are used you must quote the value. ++You can also specify the ++.B --apqns ++option to re-encipher those secure ++keys which are associated with the specified cryptographic adapters (APQNs). ++You can use wildcards for the APQN specification. ++When wildcards are used you must quote the value. ++If both option ++.B --name ++and option ++.B --apqns ++are specified then all secure keys ++contained in the key repository that match both patterns are re-enciphered. ++If all both options are omitted, then all secure keys contained in the key ++repository are re-enciphered. ++.PP ++Re-enciphering a secure key contained in the secure key repository can be ++performed \fBin-place\fP, or in \fBstaged\fP mode. ++.PP ++\fB"In-place"\fP immediately replaces the secure key in the repository with ++the re-enciphered secure key. Re-enciphering from \fBOLD\fP to \fBCURRENT\fP is ++performed in-place per default. You can use option \fB--in-place\fP to force an ++in-place re-enciphering for the \fBCURRENT\fP to \fBNEW\fP case. Be aware that ++a secure key that was re-enciphered in-place from \fBCURRENT\fP to \fBNEW\fP ++is no longer valid, until the new CCA master key has been made the current one. ++.PP ++\fBStaged\fP mode means that the re-enciphered secure key is stored in a ++separate file in the secure key repository. Thus the current secure key is still ++valid at this point. Once the new CCA master key has been set (made active), you ++must rerun the reencipher command with option \fB--complete\fP to complete the ++staged re-enciphering. Re-enciphering from \fBCURRENT\fP to \fBNEW\fP is ++performed in staged mode per default. You can use option \fB--staged\fP to force ++a staged re-enciphering for the \fBOLD\fP to \fBCURRENT\fP case. + .PP + .B Note: +-The \fBreencipher\fP command requires the IBM CCA Host Library (libcsulcca.so) ++The \fBreencipher\fP command requires the CCA host library (libcsulcca.so) + to be installed. + . ++.SS "Import existing AES secure keys into the secure key repository" ++. ++.B zkey ++.BR import | im ++.I secure\-key\-file ++.B \-\-name | \-N ++.IB key-name ++.RB [ \-\-description | \-d ++.IB description ] ++.RB [ \-\-volumes | \-l ++.IB volume1:dmname1[,volume2:dmname2[,...]] ] ++.RB [ \-\-apqns | \-a ++.IB card1.domain1[,card2.domain2[,...]] ] ++.RB [ \-\-sector-size | \-S ++.IB bytes ] ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++Use the ++.B import ++command to import an existing secure key contained in a file into the the ++secure key repository. When importing a secure key in a key repository, ++additional information can be associated with a secure key using the ++.B --description ++, ++.B --volumes ++, ++.B --apqns ++, or the ++.B --sector-size ++options. ++. ++.SS "Export AES secure keys from the secure key repository" ++. ++.B zkey ++.BR export | ex ++.I secure\-key\-file ++.B \-\-name | \-N ++.IB key-name ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++Use the ++.B export ++command to export an existing secure key contained in the secure key repository ++to a file in the file system. Specify the name of the key that is to be exported ++using the ++.B --name ++option. You cannot use wildcards. ++When wildcards are used you must quote the value. ++The exported secure key also remains in the secure key repository. ++. ++.SS "List AES secure keys contained in the secure key repository" ++. ++.B zkey ++.BR list | li ++.RB [ \-\-name | \-N ++.IB key-name ] ++.RB [ \-\-volumes | \-l ++.IB volume1[:dmname1][,volume2[:dmname2][,...]] ] ++.RB [ \-\-apqns | \-a ++.IB card1.domain1[,card2.domain2[,...]] ] ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++Use the ++.B list ++command to display a list of secure keys contained in the secure key repository. ++You can filter the displayed list by key name, associated volumes, and ++associated cryptographic adapters (APQNs). You can use wildcards for the key ++name, associated APQNs, and associated volumes. The device-mapper name of an ++associated volume can be omitted; if it is specified then only those keys are ++listed that are associated with the specified volume and device-mapper name. ++.PP ++The ++.B list ++command displays the attributes of the secure keys, such as key sizes, ++whether it is a secure key that can be used for the XTS cipher mode, the textual ++description, associated cryptographic adapters (APQNs) and volumes, the ++sector size, and timestamps for key creation, last modification and last ++re-encipherment. ++. ++.SS "Remove existing AES secure keys from the secure key repository" ++. ++.B zkey ++.BR remove | rem ++.B \-\-name | \-N ++.IB key-name ++.RB [ \-\-force | \-F ] ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++Use the ++.B remove ++command to remove an existing secure key from the secure key repository. ++Specify the name of the key that is to be removed using the ++.B --name ++option. You cannot use wildcards. The remove command prompts for ++a confirmation, unless you specify the ++.B --force ++option. ++.PP ++.B Note: ++When removing a secure key that is associated with one or multiple volumes, ++a message informs you about the associated volumes. When the secure key is ++removed, these volumes can no longer be used, unless you have a backup of the ++secure key. ++. ++.SS "Change existing AES secure keys contained the secure key repository" ++. ++.B zkey ++.BR change | ch ++.B \-\-name | \-N ++.IB key-name ++.RB [ \-\-description | \-d ++.IB description ] ++.RB [ \-\-volumes | \-l ++.IB [+|-]volume1:dmname1[,volume2:dmname2[,...]] ] ++.RB [ \-\-apqns | \-a ++.IB [+|-]card1.domain1[,card2.domain2[,...]] ] ++.RB [ \-\-sector-size | \-S ++.IB bytes ] ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++Use the ++.B change ++command to change the description, the associated volumes, the associated ++cryptographic adapters (APQNs), and the sector size of a secure key contained ++in the secure key repository. Specify the name of the key that is to be changed ++using the ++.B --name ++option. You cannot use wildcards. ++.PP ++You can set (replace), add, or ++remove volume and cryptographic adapters (APQN) associations. To set ++(replace) an association, specify the association with the ++.B --volumes ++or the ++.B --apqns ++options. To add an association, ++specify the new association prefixed with a \fI+\fP with the ++.B --volumes ++or the ++.B --apqns ++options. To remove an association, ++specify the association to remove prefixed with a \fI-\fP with the ++.B --volumes ++or the ++.B --apqns ++options. You cannot mix \fI+\fP and ++\fI-\fP in one specification. You can either add or remove (or set) the ++associations with one command. ++.PP ++.B Note: ++The secure key itself cannot be changed, only information about the secure ++key is changed. To rename a secure key, use the \fBrename\fP command. ++To re-encipher a secure key with a new CCA master key, use the \fBreencipher\fP ++command. ++. ++.SS "Rename existing AES secure keys in the secure key repository" ++. ++.B zkey ++.BR rename | ren ++.B \-\-name | \-N ++.IB key-name ++.B \-\-new-name | \-w ++.IB new-key-name ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++Use the ++.B rename ++command to rename an existing secure key in the secure key repository. ++Specify the name of the key that is to be renamed using the ++.B --name ++option and the new name using the ++.B --new-name ++option. You cannot use wildcards. ++. ++.SS "Copy (duplicate) existing AES secure keys in the secure key repository" ++. ++.B zkey ++.B copy | co ++.RB \-\-name | \-N ++.IB key-name ++.B \-\-new-key-name | \-w ++.IB new-name ++.RB [ \-\-volumes | \-l ++.IB volume1:dmname1[,volume2:dmname2[,...]] ] ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++Use the ++.B copy ++command to copy (duplicate) an existing secure key in the secure key repository. ++Specify the name of the key that is to be copied using the ++.B --name ++option and the name of the copied key using the ++.B --new-name ++option. You cannot use wildcards. ++.PP ++.B Note: ++When copying a secure key, the volume associations are not copied, because ++a specific volume can only be associated with a single secure key. Specify the ++.B --volumes ++option to associate different ++volumes with the copied secure key, or use the \fBchange\fP command to associate ++volumes afterwards. ++. ++.SS "Generate crypttab entries for volumes associated with secure AES keys" ++. ++.B zkey ++.BR crypttab | cryptt ++.RB [ \-\-volumes | \-l ++.IB volume1[:dmname1][,volume2[:dmname2][,...]] ] ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++Use the ++.B crypttab ++command to generate crypttab entries using the \fBplain\fP dm-crypt mode ++for volumes that are associated with secure keys contained in the secure key ++repository. Specify the ++.B --volumes ++option to limit the list ++of volumes where crypttab entries are generated for. You can use wildcards. ++When wildcards are used you must quote the value. ++The device-mapper name of an associated volume can be omitted; if it is ++specified then only those volumes with the specified volume and device-mapper ++name are selected. ++. ++.SS "Generate cryptsetup commands for volumes associated with secure AES keys" ++. ++.B zkey ++.BR cryptsetup | crypts ++.RB [ \-\-volumes | \-l ++.IB volume1[:dmname1][,volume2[:dmname2][,...]] ] ++.RB [ \-\-run | \-r ] ++.RB [ \-\-verbose | \-V ] ++. ++.PP ++Use the ++.B cryptsetup ++command to generate \fBcryptsetup plainOpen\fP commands for volumes that are ++associated with secure keys contained in the secure key repository. Specify the ++.B --volumes ++option to limit the list ++of volumes where cryptsetup commands are generated for. You can use wildcards. ++When wildcards are used you must quote the value. ++The device-mapper name of an associated volume can be omitted; if it is ++specified then only those volumes with the specified volume and device-mapper ++name are selected. Specify the ++.B --run ++option to run the generated cryptsetup commands. ++. ++. + . + . + .SH OPTIONS +@@ -139,13 +546,13 @@ to be installed. + .TP + .BR \-k ", " \-\-keybits\~\fIsize\fP + Specifies the size of the AES key to be generated in bits. +-Valid sizes are 128, 192, and 256 bits. Secure keys for use with the ++Valid values are 128, 192, and 256. Secure keys for use with the + XTS cipher mode can only use keys of 128 or 256 bits. + The default is 256 bits. + .TP + .BR \-x ", " \-\-xts +-Generates a secure AES key for the XTS cipher mode. A secure AES key for +-the XTS cipher mode consist of two concatenated secure keys. ++Generates a secure AES key for the XTS cipher mode that consist of two ++concatenated secure keys. + .TP + .BR \-c ", " \-\-clearkey\~\fIclear\-key\-file\fP + Specifies a file path that contains the clear AES key in binary form. +@@ -154,6 +561,49 @@ determines the size of the AES key. If + is specified, the size of the specified file must match the specified + key size. Valid file sizes are of 16, 24, or 32 bytes, and of 32 or 64 + bytes for keys to be used with the XTS cipher mode. ++.TP ++.BR \-N ", " \-\-name\~\fIkey-name\fP ++Specifies the name of the secure key in the secure key repository. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-d ", " \-\-description\~\fIdescription\fP ++Specifies a textual description for the secure key in the secure key repository. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-l ", " \-\-volumes\~\fIvolume1:dmname1[,volume2:dmname2[,...]]\fP ++Specifies a comma-separated list of volumes (block devices) that are ++associated with the secure AES key in the repository. These volumes are to be ++encrypted using dm-crypt with the secure AES key. The volume association also ++contains the device-mapper name, separated by a colon, used with dm-crypt. ++A specific volume can only be associated with a single secure key. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP ++Specifies a comma-separated list of cryptographic adapters in CCA ++coprocessor mode (APQN) which are associated with the secure AES key in the ++repository. Each APQN association specifies a card and domain number separated ++by a period (like lszcrypt displays it). When at least one APQN is specified, ++then the first one is used to generate the key. If no APQNs are specified, ++then an APQN is selected automatically. All specified APQNs must be online. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-S ", " \-\-sector-size\~\fIbytes\fP ++Specifies the sector size in bytes used with dm-crypt. It must be a power of two ++and in the range 512 - 4096 bytes. If omitted, the system default sector size ++is used. ++This option is only used for secure keys contained in the secure key repository. ++. ++. ++. ++.SS "Options for the validate command" ++.TP ++.BR \-N ", " \-\-name\~\fIkey-name\fP ++Specifies the name of the secure key in the secure key repository. You can ++use wildcards to select multiple secure keys in the secure key repository. ++When wildcards are used you must quote the value. ++This option is only used for secure keys contained in the secure key repository. ++. ++. + . + .SS "Options for the reencipher command" + .TP +@@ -168,7 +618,256 @@ master key in the OLD register with the + .BR \-f ", " \-\-output\~\fIoutput\-file\fP + Specifies the name of the output file to which the re-enciphered secure key + is written. If this option is omitted, the re-enciphered secure key +-is replaced in the file that currently contains the secure key. ++is replaced in the file that currently contains the secure key. This option is ++only used for secure keys stored in a file in the file system. It is not valid ++for keys contained in the secure key repository. ++.TP ++.BR \-N ", " \-\-name\~\fIkey-name\fP ++Specifies the name of the secure key in the secure key repository. You can ++use wildcards to select multiple secure keys in the secure key repository. ++When wildcards are used you must quote the value. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP ++Specifies a comma-separated list of cryptographic adapters in CCA ++coprocessor mode (APQNs). You can use wildcards in the APQN specification. ++All secure keys contained in the secure key repository ++which are associated with the specified APQNs are re-enciphered. ++Each APQN specifies a card and domain number separated by a period (like ++lszcrypt displays it). ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-i ", " \-\-in-place ++Forces an in-place re-enciphering of a secure AES key contained in the secure ++key repository. "In-place" immediately replaces the secure key in the repository ++with the re-enciphered secure key. ++Re-enciphering from OLD to CURRENT is performed in-place per default. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-s ", " \-\-staged ++Forces that the re-enciphering of a secure AES key contained in the secure key ++repository is performed in staged mode. Staged mode means that the re-enciphered ++secure key is stored in a separate file in the secure key repository. Thus the ++current secure key is still valid at this point. Once the new CCA master key has ++been set (made active), you must rerun the reencipher command with option ++\fB--complete\fP to complete the staged re-enciphering. ++Re-enciphering from CURRENT to NEW is performed in staged mode per default. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-p ", " \-\-complete ++Completes a staged re-enciphering. Use this option after the new CCA master key ++has been set (made active). This option replaces the secure key by its ++re-enciphered version in the secure key repository. ++This option is only used for secure keys contained in the secure key repository. ++. ++. ++. ++. ++.SS "Options for the import command" ++.TP ++.BR \-N ", " \-\-name\~\fIkey-name\fP ++Specifies the name of the secure key in the secure key repository. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-d ", " \-\-description\~\fIdescription\fP ++Specifies a textual description for the secure key in the secure key repository. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-l ", " \-\-volumes\~\fIvolume1:dmname1[,volume2:dmname2[,...]]\fP ++Specifies a comma-separated list of volumes (block devices) which are ++associated with the secure AES key in the repository. These volumes are to be ++encrypted using dm-crypt with the secure AES key. The volume association also ++contains the device-mapper name, separated by a colon, used with dm-crypt. ++A specific volume can only be associated with a single secure key. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP ++Specifies a comma-separated list of cryptographic adapters in CCA ++coprocessor mode (APQN) which are associated with the secure AES key in the ++repository. Each APQN association specifies a card and domain number separated ++by a period (like lszcrypt displays it). All specified APQNs must be online. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-S ", " \-\-sector-size\~\fIbytes\fP ++Specifies the sector size in bytes used with dm-crypt. It must be a power of two ++and in the range 512 - 4096 bytes. If omitted, the system default sector size ++is used. ++This option is only used for secure keys contained in the secure key repository. ++. ++. ++. ++.SS "Options for the export command" ++.TP ++.BR \-N ", " \-\-name\~\fIkey-name\fP ++Specifies the name of the secure key in the secure key repository. You cannot ++use wildcards. ++This option is only used for secure keys contained in the secure key repository. ++. ++. ++. ++.SS "Options for the list command" ++.TP ++.BR \-N ", " \-\-name\~\fIkey-name\fP ++Specifies the name of the secure key in the secure key repository. You can ++use wildcards to select multiple secure keys in the secure key repository. ++When wildcards are used you must quote the value. ++Only keys with names that match the pattern are listed. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-l ", " \-\-volumes\~\fIvolume1[:dmname1][,volume2[:dmname2][,...]]\fP ++Specifies a comma-separated list of volumes (block devices) which are ++associated with the secure AES key in the repository. Only those keys are ++listed, which are associated with the specified volumes. ++The volume association also contains the device-mapper name, separated by a ++colon, used with dm-crypt. You can omit the device-mapper name; if it is ++specified then only those keys are listed that are associated with the ++specified volume and device-mapper name. You can use wildcards to specify ++the volumes and device-mapper names. ++When wildcards are used you must quote the value. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-a ", " \-\-apqns\~\fIcard1.domain1[,card2.domain2[,...]]\fP ++Specifies a comma-separated list of cryptographic adapters in CCA ++coprocessor mode (APQN) which are associated with the secure AES key in the ++repository. Only those keys are listed, which are associated with the specified ++APQNs. Each APQN association specifies a card and domain number separated ++by a period (like lszcrypt displays it). You can use wildcards in the APQN ++specification. ++This option is only used for secure keys contained in the secure key repository. ++. ++. ++. ++.SS "Options for the remove command" ++.TP ++.BR \-N ", " \-\-name\~\fIkey-name\fP ++Specifies the name of the secure key in the secure key repository. You cannot ++use wildcards. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-F ", " \-\-force\fP ++The user is prompted to confirm the removal of a secure key from the secure ++key repository. Use this option to remove a secure key without prompting for ++a confirmation. ++This option is only used for secure keys contained in the secure key repository. ++. ++. ++. ++.SS "Options for the change command" ++.TP ++.BR \-N ", " \-\-name\~\fIkey-name\fP ++Specifies the name of the secure key in the secure key repository. You cannot ++use wildcards. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-d ", " \-\-description\~\fIdescription\fP ++Specifies a textual description for the secure key in the secure key repository. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-l ", " \-\-volumes\~\fI[+|-]volume1:dmname1[,volume2:dmname2[,...]]\fP ++Specifies a comma-separated list of volumes (block devices) which are ++associated with the secure AES key in the repository. These volumes are to be ++encrypted using dm-crypt with the secure AES key. The volume association also ++contains the device-mapper name, separated by a colon, used with dm-crypt. ++To add a volume to the associated volumes, prefix the volume with a \fI+\fP. ++To remove a volume from the associated volumes, prefix the volume with a \fI-\fP. ++To set (replace) the volume association do not specify a prefix. ++You cannot mix \fI+\fP and \fI-\fP in one specification. You can either add or ++remove (or set) the associations with one command. ++A specific volume can only be associated with a single secure key. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-a ", " \-\-apqns\~\fI[+|-]card1.domain1[,card2.domain2[,...]]\fP ++Specifies a comma-separated list of cryptographic adapters in CCA ++coprocessor mode (APQN) which are associated with the secure AES key in the ++repository. Each APQN association specifies a card and domain number separated ++by a period (like lszcrypt displays it). ++To add an APQN to the associated APQNs, prefix the APQN with a \fI+\fP. ++To remove an APQN from the associated APQNs, prefix the APQN with a \fI-\fP. ++To set (replace) the APQN association do not specify a prefix. ++You cannot mix \fI+\fP and \fI-\fP in one specification. You can either add or ++remove (or set) the associations with one command. ++All APQNs being added or set (replaced) must be online. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-S ", " \-\-sector-size\~\fIbytes\fP ++Specifies the sector size in bytes used with dm-crypt. It must be a power of two ++and in the range 512 - 4096 bytes. If omitted, the system default sector size ++is used. Specify \fI0\fP to un-set the sector size so that the system default ++is used. ++This option is only used for secure keys contained in the secure key repository. ++. ++. ++. ++.SS "Options for the rename command" ++.TP ++.BR \-N ", " \-\-name\~\fIkey-name\fP ++Specifies the name of the secure key in the secure key repository. You cannot ++use wildcards. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-w ", " \-\-new-name\~\fInew-key-name\fP ++Specifies the new name of the secure key in the secure key repository. ++This option is only used for secure keys contained in the secure key repository. ++. ++. ++. ++.SS "Options for the copy command" ++.TP ++.BR \-N ", " \-\-name\~\fIkey-name\fP ++Specifies the name of the secure key to be copied in the secure key repository. ++You cannot use wildcards. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-w ", " \-\-new-name\~\fInew-key-name\fP ++Specifies the new name of the secure key in the secure key repository. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-l ", " \-\-volumes\~\fIvolume1:dmname1,volume2:dmname2[,...]]\fP ++Volume associations are not copied, because a volume can only be associated ++with a single secure key. To associate different volumes with the copied ++secure AES key, specify a comma-separated list of volumes (block devices). ++These volumes are to be encrypted using dm-crypt with the secure AES key. The ++volume association also contains the device-mapper name, separated by a colon, ++used with dm-crypt. ++This option is only used for secure keys contained in the secure key repository. ++. ++. ++. ++.SS "Options for the crypttab command" ++.TP ++.BR \-l ", " \-\-volumes\~\fIvolume1[:dmname1][,volume2[:dmname2][,...]]\fP ++Specifies a comma-separated list of volumes (block devices) which are ++associated with secure AES keys in the repository. ++The volume association also contains the device-mapper name, separated by a ++colon, used with dm-crypt. You can omit the device-mapper name; if it is ++specified then only those keys are selected that are associated with the ++specified volume and device-mapper name. You can use wildcards to specify ++the volumes and device-mapper names. ++When wildcards are used you must quote the value. ++This option is only used for secure keys contained in the secure key repository. ++. ++. ++. ++.SS "Options for the cryptsetup command" ++.TP ++.BR \-l ", " \-\-volumes\~\fIvolume1[:dmname1][,volume2[:dmname2][,...]]\fP ++Specifies a comma-separated list of volumes (block devices) which are ++associated with secure AES keys in the repository. ++The volume association also contains the device-mapper name, separated by a ++colon, used with dm-crypt. You can omit the device-mapper name; if it is ++specified then only those keys are selected that are associated with the ++specified volume and device-mapper name. You can use wildcards to specify ++the volumes and device-mapper names. ++When wildcards are used you must quote the value. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-r ", " \-\-run\fP ++Runs the generated cryptsetup commands. When an execution of a cryptsetup ++command fails, no further cryptsetup commands are executed, and zkey ends ++with an error. ++This option is only used for secure keys contained in the secure key repository. ++. ++. + . + .SS "General options" + .TP +@@ -186,16 +885,25 @@ Displays version information and exits. + .SH EXAMPLES + .TP + .B zkey generate seckey.bin +-Generates a 256-bit secure AES key and stores it in file 'seckey.bin'. ++Generates a random 256-bit secure AES key and stores it in file 'seckey.bin'. + .TP + .B zkey generate seckey.bin \-\-keybits 128 \-\-xts +-Generates a 128-bit secure AES key for the XTS cipher mode and stores it ++Generates a random 128-bit secure AES key for the XTS cipher mode and stores it + in file 'seckey.bin'. + .TP + .B zkey generate seckey.bin \-\-clearkey clearkey.bin + Generates a secure AES key from the clear key in file 'clearkey.bin' and + stores it in file 'seckey.bin'. + .TP ++.B zkey generate --name seckey ++Generates a random 256-bit secure AES key and stores it in the secure key ++repository under the name 'seckey'. ++.TP ++.B zkey generate --name seckey --volumes /dev/dasdc1:encvol --apqns 03.004c ++Generates a random 256-bit secure AES key and stores it in the secure key ++repository under the name 'seckey' and associates it with block ++device '/dev/dasdc1' and device-mapper name 'encvol', and APQN '03.004c'. ++.TP + .B zkey reencipher seckey.bin \-\-from\-old + Re-enciphers the secure key in file 'seckey.bin' which is currently enciphered + with the master key in the OLD register with the master key in the CURRENT +@@ -207,5 +915,49 @@ Re-enciphers the secure key in file 'sec + with the master key in the CURRENT register with the master key in the NEW + register, and saves the re-enciphered secure key to file 'seckey2.bin'. + .TP ++.B zkey reencipher --name seckey ++Re-enciphers the secure key 'seckey' in the secure key repository. ++.TP ++.B zkey reencipher --apqns 03.004c ++Re-enciphers all secure keys contained in the secure key repository that are ++associated with APQN '03.004c'. ++.TP + .B zkey validate seckey.bin + Validates the secure key in file 'seckey.bin' and displays its attributes. ++.TP ++.B zkey validate --name seckey ++Validates the secure key 'seckey' in the secure key repository and displays its ++attributes. ++.TP ++.B zkey list ++Lists all secure keys in the secure key repository and displays its ++attributes. ++.TP ++.B zkey list --name '*key' ++Lists all secure keys in the secure key repository with names ending with 'key' ++and displays its attributes. ++.TP ++.B zkey change --name seckey --volumes +/dev/dasdc2:encvol2 ++Changes the secure key 'seckey' in the secure key repository and adds ++volume '/dev/dasdc2' with device-mapper name 'encvol2' to the list of associated ++volumes of this secure key. ++.TP ++.B zkey change --name seckey --apqns -03.004c ++Changes the secure key 'seckey' in the secure key repository and removes ++APQN '03.004c' from the list of associated APQNs of this secure key. ++.TP ++.B zkey crypttab --volumes '/dev/dasdc*' ++Generates crypttab entries for all volumes that match the pattern '/dev/dasdc*'. ++.TP ++.B zkey cryptsetup --volumes '*:enc_dasd' ++Generates cryptsetup commands for the volumes that uses the device-mapper ++name 'enc_dasd'. ++. ++.SH ENVIRONMENT ++.TP ++.BR ZKEY_REPOSITORY ++If ++.B $ZKEY_REPOSITORY ++is set, it specifies the location of the secure key repository. ++If it is not set, then the the default location of the secure key ++repository is \fB/etc/zkey/repository\fP. +\ No newline at end of file diff --git a/s390-tools-sles15sp1-0009-zkey-let-packaging-create-the-zkeyadm-group-and-perm.patch b/s390-tools-sles15sp1-0009-zkey-let-packaging-create-the-zkeyadm-group-and-perm.patch new file mode 100644 index 0000000..4194edf --- /dev/null +++ b/s390-tools-sles15sp1-0009-zkey-let-packaging-create-the-zkeyadm-group-and-perm.patch @@ -0,0 +1,38 @@ +Subject: zkey: let packaging create the zkeyadm group and permission setup +From: Philipp Rudo + +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: 3eb9af9c97c98e9f9665af1c5e671266400aaafc +Problem-ID: SEC1800 + +Upstream-Description: + + zkey: let packaging create the zkeyadm group and permission setup + + Signed-off-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Philipp Rudo +--- + zkey/Makefile | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -36,9 +36,8 @@ install: all + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zkey $(DESTDIR)$(USRBINDIR) + $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1 + $(INSTALL) -m 644 -c zkey.1 $(DESTDIR)$(MANDIR)/man1 +- getent group zkeyadm >/dev/null || groupadd -r zkeyadm +- $(INSTALL) -d -g zkeyadm -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey +- $(INSTALL) -d -g zkeyadm -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey/repository ++ $(INSTALL) -d -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey ++ $(INSTALL) -d -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey/repository + + endif + diff --git a/s390-tools-sles15sp1-0010-zkey-Update-README-to-add-info-about-packaging-requi.patch b/s390-tools-sles15sp1-0010-zkey-Update-README-to-add-info-about-packaging-requi.patch new file mode 100644 index 0000000..44c0af0 --- /dev/null +++ b/s390-tools-sles15sp1-0010-zkey-Update-README-to-add-info-about-packaging-requi.patch @@ -0,0 +1,34 @@ +Subject: zkey: Update README to add info about packaging requirements +From: Philipp Rudo + +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: 80b66da1d81793232646d2504c4d4c0ec94170f1 +Problem-ID: SEC1800 + +Upstream-Description: + + zkey: Update README to add info about packaging requirements + + Signed-off-by: Ingo Franzki + Signed-off-by: Jan Höppner + + +Signed-off-by: Philipp Rudo +--- + README.md | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/README.md ++++ b/README.md +@@ -371,3 +371,7 @@ the different tools are provided: + For building the zkey tools you need openssl version 0.9.7 or newer installed + (openssl-devel.rpm). Tip: you may skip the zkey build by adding + `HAVE_OPENSSL=0` to the make invocation. ++ A new group 'zkeyadm' needs to be created and all users intending to use the ++ tool must be added to this group. The owner of the default key repository ++ '/etc/zkey/repository' must be set to group 'zkeyadm' with write permission ++ for this group. diff --git a/s390-tools-sles15sp1-0011-zkey-Typo-in-message.patch b/s390-tools-sles15sp1-0011-zkey-Typo-in-message.patch new file mode 100644 index 0000000..3554ae6 --- /dev/null +++ b/s390-tools-sles15sp1-0011-zkey-Typo-in-message.patch @@ -0,0 +1,34 @@ +Subject: zkey: Typo in message +From: Ingo Franzki + +Summary: zkey: Support CCA master key change with LUKS2 volumes using paes +Description: Support the usage of protected key crypto for dm-crypt disks in + LUKS2 format by providing a tool allowing to re-encipher a + secure LUKS2 volume key when the CCA master key is changed +Upstream-ID: dec58c349e794f6333771457d9dcb9c0768fe28e +Problem-ID: SEC1424.1 + +Upstream-Description: + + zkey: Typo in message + + Signed-off-by: Ingo Franzki + Signed-off-by: Jan Höppner + + +Signed-off-by: Ingo Franzki +--- + zkey/keystore.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/zkey/keystore.c ++++ b/zkey/keystore.c +@@ -2319,7 +2319,7 @@ static int _keystore_process_reencipher( + + if (params.complete) { + if (!_keystore_reencipher_key_exists(file_names)) { +- warnx("Staged re-enciphering in not pending for key " ++ warnx("Staged re-enciphering is not pending for key " + "'%s', skipping", + name); + info->num_skipped++; diff --git a/s390-tools-sles15sp1-0012-zkey-Fix-memory-leak.patch b/s390-tools-sles15sp1-0012-zkey-Fix-memory-leak.patch new file mode 100644 index 0000000..fde16e8 --- /dev/null +++ b/s390-tools-sles15sp1-0012-zkey-Fix-memory-leak.patch @@ -0,0 +1,102 @@ +Subject: zkey: Fix memory leak +From: Ingo Franzki + +Summary: zkey: Support CCA master key change with LUKS2 volumes using paes +Description: Support the usage of protected key crypto for dm-crypt disks in + LUKS2 format by providing a tool allowing to re-encipher a + secure LUKS2 volume key when the CCA master key is changed +Upstream-ID: d6a96f07c1a0ba9b1a559561698f82f5a19829ff +Problem-ID: SEC1424.1 + +Upstream-Description: + + zkey: Fix memory leak + + The APQN check routine as well as the properties helper functions + do not free all memory that they allocated. + + Signed-off-by: Ingo Franzki + Signed-off-by: Jan Höppner + + +Signed-off-by: Ingo Franzki +--- + zkey/keystore.c | 22 +++++++++++++++------- + zkey/properties.c | 5 +++++ + 2 files changed, 20 insertions(+), 7 deletions(-) + +--- a/zkey/keystore.c ++++ b/zkey/keystore.c +@@ -981,25 +981,33 @@ static int _keystore_apqn_check(const ch + rc = regexec(®_buf, apqn, (size_t) 1, pmatch, 0); + if (rc != 0) { + warnx("the APQN '%s' is not valid", apqn); +- return -EINVAL; ++ rc = -EINVAL; ++ goto out; + } + +- if (sscanf(apqn, "%x.%x", &card, &domain) != 2) +- return -EINVAL; ++ if (sscanf(apqn, "%x.%x", &card, &domain) != 2) { ++ rc = -EINVAL; ++ goto out; ++ } + + util_asprintf(normalized, "%02x.%04x", card, domain); + +- if (remove) +- return 0; ++ if (remove) { ++ rc = 0; ++ goto out; ++ } + + rc = _keystore_is_apqn_online(card, domain); + if (rc != 1) { + warnx("The APQN %02x.%04x is %s", card, domain, + rc == -1 ? "not a CCA card" : "not online"); +- return -EIO; ++ rc = -EIO; ++ goto out; + } + +- return 0; ++out: ++ regfree(®_buf); ++ return rc; + } + + +--- a/zkey/properties.c ++++ b/zkey/properties.c +@@ -149,6 +149,7 @@ void properties_free(struct properties * + free(property->name); + free(property->value); + util_list_remove(&properties->list, property); ++ free(property); + } + + free(properties); +@@ -259,6 +260,7 @@ int properties_remove(struct properties + free(property->name); + free(property->value); + util_list_remove(&properties->list, property); ++ free(property); + return 0; + } + +@@ -614,10 +616,13 @@ char *str_list_remove(const char *str_li + */ + void str_list_free_string_array(char **strings) + { ++ char **list = strings; ++ + util_assert(strings != NULL, "Internal error: strings is NULL"); + + while (*strings != NULL) { + free((void *)*strings); + strings++; + } ++ free(list); + } diff --git a/s390-tools-sles15sp1-0013-zkey-Fix-APQN-validation-routine.patch b/s390-tools-sles15sp1-0013-zkey-Fix-APQN-validation-routine.patch new file mode 100644 index 0000000..38ab4db --- /dev/null +++ b/s390-tools-sles15sp1-0013-zkey-Fix-APQN-validation-routine.patch @@ -0,0 +1,47 @@ +Subject: zkey: Fix APQN validation routine +From: Ingo Franzki + +Summary: zkey: Support CCA master key change with LUKS2 volumes using paes +Description: Support the usage of protected key crypto for dm-crypt disks in + LUKS2 format by providing a tool allowing to re-encipher a + secure LUKS2 volume key when the CCA master key is changed +Upstream-ID: 344965bd296f434ccbd9ad5b16427590b988d480 +Problem-ID: SEC1424.1 + +Upstream-Description: + + zkey: Fix APQN validation routine + + When a zkey generate or change command is used to associate one + or multiple APQNs the command succeeds, but no key is generated + and no APQNs are associated, because the return code returned by + _keystore_apqn_check() is wrong. + + Signed-off-by: Ingo Franzki + Signed-off-by: Jan Höppner + + +Signed-off-by: Ingo Franzki +--- + zkey/keystore.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/zkey/keystore.c ++++ b/zkey/keystore.c +@@ -986,6 +986,7 @@ static int _keystore_apqn_check(const ch + } + + if (sscanf(apqn, "%x.%x", &card, &domain) != 2) { ++ warnx("the APQN '%s' is not valid", apqn); + rc = -EINVAL; + goto out; + } +@@ -1003,6 +1004,8 @@ static int _keystore_apqn_check(const ch + rc == -1 ? "not a CCA card" : "not online"); + rc = -EIO; + goto out; ++ } else { ++ rc = 0; + } + + out: diff --git a/s390-tools-sles15sp1-0014-zkey-Fix-generate-and-import-leaving-key-in-an-incon.patch b/s390-tools-sles15sp1-0014-zkey-Fix-generate-and-import-leaving-key-in-an-incon.patch new file mode 100644 index 0000000..7698aa4 --- /dev/null +++ b/s390-tools-sles15sp1-0014-zkey-Fix-generate-and-import-leaving-key-in-an-incon.patch @@ -0,0 +1,47 @@ +Subject: zkey: Fix generate and import leaving key in an inconsistent state +From: Ingo Franzki + +Summary: zkey: Support CCA master key change with LUKS2 volumes using paes +Description: Support the usage of protected key crypto for dm-crypt disks in + LUKS2 format by providing a tool allowing to re-encipher a + secure LUKS2 volume key when the CCA master key is changed +Upstream-ID: 672548ce30f61e94c8465a560a54a4a8fe568c06 +Problem-ID: SEC1424.1 + +Upstream-Description: + + zkey: Fix generate and import leaving key in an inconsistent state + + When a volume or APQN association is made while generating or + importing a key, and a duplicate association is detected, then + this may leave the key in an inconsistent state. + + Signed-off-by: Ingo Franzki + Signed-off-by: Jan Höppner + + +Signed-off-by: Ingo Franzki +--- + zkey/keystore.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/zkey/keystore.c ++++ b/zkey/keystore.c +@@ -1534,7 +1534,7 @@ int keystore_generate_key(struct keystor + out_free_props: + if (key_props != NULL) + properties_free(key_props); +- if (rc != 0 && rc != -EEXIST) ++ if (rc != 0) + remove(file_names.skey_filename); + out_free_key_filenames: + _keystore_free_key_filenames(&file_names); +@@ -1617,7 +1617,7 @@ int keystore_import_key(struct keystore + out_free_props: + if (key_props != NULL) + properties_free(key_props); +- if (rc != 0 && rc != -EEXIST) ++ if (rc != 0) + remove(file_names.skey_filename); + out_free_key_filenames: + _keystore_free_key_filenames(&file_names); diff --git a/s390-tools-sles15sp1-0015-zkey-Add-zkey-cryptsetup-tool.patch b/s390-tools-sles15sp1-0015-zkey-Add-zkey-cryptsetup-tool.patch new file mode 100644 index 0000000..c3a66bd --- /dev/null +++ b/s390-tools-sles15sp1-0015-zkey-Add-zkey-cryptsetup-tool.patch @@ -0,0 +1,2584 @@ +Subject: zkey: Add zkey-cryptsetup tool +From: Ingo Franzki + +Summary: zkey: Support CCA master key change with LUKS2 volumes using paes +Description: Support the usage of protected key crypto for dm-crypt disks in + LUKS2 format by providing a tool allowing to re-encipher a + secure LUKS2 volume key when the CCA master key is changed +Upstream-ID: 4eb80d14a0554c8a404f06beeb53522e45d5df6e +Problem-ID: SEC1424.1 + +Upstream-Description: + + zkey: Add zkey-cryptsetup tool + + The zkey-cryptsetup tool is used to validate and re-encipher + secure AES volume keys of volumes encrypted with LUKS2 and + the paes cipher. + + Signed-off-by: Ingo Franzki + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Ingo Franzki +--- + zkey/Makefile | 12 + zkey/pkey.c | 158 +++ + zkey/pkey.h | 7 + zkey/zkey-cryptsetup.c | 2270 +++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 2444 insertions(+), 3 deletions(-) + +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -18,9 +18,8 @@ check_dep: + "HAVE_OPENSSL=0") + + CPPFLAGS += -I../include +-LDLIBS += -ldl -lcrypto + +-all: check_dep zkey ++all: check_dep zkey zkey-cryptsetup + + libs = $(rootdir)/libutil/libutil.a + +@@ -28,12 +27,19 @@ zkey.o: zkey.c pkey.h misc.h + pkey.o: pkey.c pkey.h + properties.o: properties.c properties.h + keystore.o: keystore.c keystore.h properties.h ++zkey-cryptsetup.o: zkey-cryptsetup.c pkey.h misc.h + ++zkey: LDLIBS = -ldl -lcrypto + zkey: zkey.o pkey.o properties.o keystore.o $(libs) + ++zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c ++zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(libs) ++ ++ + install: all + $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zkey $(DESTDIR)$(USRBINDIR) ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zkey-cryptsetup $(DESTDIR)$(USRBINDIR) + $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1 + $(INSTALL) -m 644 -c zkey.1 $(DESTDIR)$(MANDIR)/man1 + $(INSTALL) -d -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey +@@ -42,6 +48,6 @@ install: all + endif + + clean: +- rm -f *.o zkey ++ rm -f *.o zkey zkey-cryptsetup + + .PHONY: all install clean +--- a/zkey/pkey.c ++++ b/zkey/pkey.c +@@ -11,11 +11,13 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + #include ++#include + #include + #include + +@@ -25,6 +27,12 @@ + + #include "pkey.h" + ++#ifndef AF_ALG ++#define AF_ALG 38 ++#endif ++#ifndef SOL_ALG ++#define SOL_ALG 279 ++#endif + + #define pr_verbose(verbose, fmt...) do { \ + if (verbose) \ +@@ -34,6 +42,8 @@ + #define DOUBLE_KEYSIZE_FOR_XTS(keysize, xts) ((xts) ? 2 * (keysize) : (keysize)) + #define HALF_KEYSIZE_FOR_XTS(keysize, xts) ((xts) ? (keysize) / 2 : (keysize)) + ++#define MAX_CIPHER_LEN 32 ++ + /* + * Definitions for the CCA library + */ +@@ -367,6 +377,8 @@ int generate_secure_key_random(int pkey_ + if (rc < 0) { + rc = -errno; + warnx("Failed to generate a secure key: %s", strerror(errno)); ++ warnx("Make sure that all available CCA crypto adapters are " ++ "setup with the same master key"); + goto out; + } + +@@ -378,6 +390,8 @@ int generate_secure_key_random(int pkey_ + rc = -errno; + warnx("Failed to generate a secure key: %s", + strerror(errno)); ++ warnx("Make sure that all available CCA crypto " ++ "adapters are setup with the same master key"); + goto out; + } + +@@ -465,6 +479,8 @@ int generate_secure_key_clear(int pkey_f + rc = -errno; + warnx("Failed to generate a secure key from a " + "clear key: %s", strerror(errno)); ++ warnx("Make sure that all available CCA crypto adapters are " ++ "setup with the same master key"); + goto out; + } + +@@ -479,6 +495,8 @@ int generate_secure_key_clear(int pkey_f + rc = -errno; + warnx("Failed to generate a secure key from " + "a clear key: %s", strerror(errno)); ++ warnx("Make sure that all available CCA crypto " ++ "adapters are setup with the same master key"); + goto out; + } + +@@ -746,3 +764,143 @@ int validate_secure_key(int pkey_fd, + + return 0; + } ++ ++/** ++ * Generate a key verification pattern of a secure key by encrypting the all ++ * zero message with the secure key using the AF_ALG interface ++ * ++ * @param[in] key the secure key token ++ * @param[in] key_size the size of the secure key ++ * @param[in] vp buffer where the verification pattern is returned ++ * @param[in] vp_len the size of the buffer ++ * @param[in] verbose if true, verbose messages are printed ++ * ++ * @returns 0 on success, a negative errno in case of an error ++ */ ++int generate_key_verification_pattern(const char *key, size_t key_size, ++ char *vp, size_t vp_len, bool verbose) ++{ ++ int tfmfd = -1, opfd = -1, rc = 0; ++ char null_msg[ENC_ZERO_LEN]; ++ char enc_zero[ENC_ZERO_LEN]; ++ struct af_alg_iv *alg_iv; ++ struct cmsghdr *header; ++ uint32_t *type; ++ ssize_t len; ++ size_t i; ++ ++ struct sockaddr_alg sa = { ++ .salg_family = AF_ALG, ++ .salg_type = "skcipher", ++ }; ++ struct iovec iov = { ++ .iov_base = (void *)null_msg, ++ .iov_len = sizeof(null_msg), ++ }; ++ int iv_msg_size = CMSG_SPACE(sizeof(*alg_iv) + PAES_BLOCK_SIZE); ++ char buffer[CMSG_SPACE(sizeof(*type)) + iv_msg_size]; ++ struct msghdr msg = { ++ .msg_control = buffer, ++ .msg_controllen = sizeof(buffer), ++ .msg_iov = &iov, ++ .msg_iovlen = 1, ++ }; ++ ++ if (vp_len < VERIFICATION_PATTERN_LEN) { ++ rc = -EMSGSIZE; ++ goto out; ++ } ++ ++ snprintf((char *)sa.salg_name, sizeof(sa.salg_name), "%s(paes)", ++ key_size > SECURE_KEY_SIZE ? "xts" : "cbc"); ++ ++ tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0); ++ if (tfmfd < 0) { ++ rc = -errno; ++ pr_verbose(verbose, "Failed to open an AF_ALG socket"); ++ goto out; ++ } ++ ++ if (bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { ++ rc = -errno; ++ pr_verbose(verbose, "Failed to bind the AF_ALG socket, " ++ "salg_name='%s' ", sa.salg_name); ++ goto out; ++ } ++ ++ if (setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, key, ++ key_size) < 0) { ++ rc = -errno; ++ pr_verbose(verbose, "Failed to set the key"); ++ goto out; ++ } ++ ++ opfd = accept(tfmfd, NULL, 0); ++ if (opfd < 0) { ++ rc = -errno; ++ pr_verbose(verbose, "Failed to accept on the AF_ALG socket"); ++ goto out; ++ } ++ ++ memset(null_msg, 0, sizeof(null_msg)); ++ memset(buffer, 0, sizeof(buffer)); ++ ++ header = CMSG_FIRSTHDR(&msg); ++ if (header == NULL) { ++ pr_verbose(verbose, "Failed to obtain control message header"); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ header->cmsg_level = SOL_ALG; ++ header->cmsg_type = ALG_SET_OP; ++ header->cmsg_len = CMSG_LEN(sizeof(*type)); ++ type = (void *)CMSG_DATA(header); ++ *type = ALG_OP_ENCRYPT; ++ ++ header = CMSG_NXTHDR(&msg, header); ++ if (header == NULL) { ++ pr_verbose(verbose, "Failed to obtain control message " ++ "header"); ++ rc = -EINVAL; ++ goto out; ++ } ++ header->cmsg_level = SOL_ALG; ++ header->cmsg_type = ALG_SET_IV; ++ header->cmsg_len = iv_msg_size; ++ alg_iv = (void *)CMSG_DATA(header); ++ alg_iv->ivlen = PAES_BLOCK_SIZE; ++ memcpy(alg_iv->iv, null_msg, PAES_BLOCK_SIZE); ++ ++ len = sendmsg(opfd, &msg, 0); ++ if (len != ENC_ZERO_LEN) { ++ pr_verbose(verbose, "Failed to send to the AF_ALG socket"); ++ rc = -errno; ++ goto out; ++ } ++ ++ len = read(opfd, enc_zero, sizeof(enc_zero)); ++ if (len != ENC_ZERO_LEN) { ++ pr_verbose(verbose, "Failed to receive from the AF_ALG socket"); ++ rc = -errno; ++ goto out; ++ } ++ ++ memset(vp, 0, vp_len); ++ for (i = 0; i < sizeof(enc_zero); i++) ++ sprintf(&vp[i * 2], "%02x", enc_zero[i]); ++ ++ pr_verbose(verbose, "Key verification pattern: %s", vp); ++ ++out: ++ if (opfd != -1) ++ close(opfd); ++ if (tfmfd != -1) ++ close(tfmfd); ++ ++ if (rc != 0) ++ pr_verbose(verbose, "Failed to generate the key verification " ++ "pattern: %s", strerror(-rc)); ++ ++ return rc; ++} +--- a/zkey/pkey.h ++++ b/zkey/pkey.h +@@ -93,6 +93,10 @@ typedef void (*t_CSNBKTC)(long *return_c + unsigned char *rule_array, + unsigned char *key_identifier); + ++#define PAES_BLOCK_SIZE 16 ++#define ENC_ZERO_LEN (2 * PAES_BLOCK_SIZE) ++#define VERIFICATION_PATTERN_LEN (2 * ENC_ZERO_LEN + 1) ++ + int load_cca_library(void **lib_csulcca, t_CSNBKTC *dll_CSNBKTC, bool verbose); + + int open_pkey_device(bool verbose); +@@ -122,4 +126,7 @@ int key_token_change(t_CSNBKTC dll_CSNBK + u8 *secure_key, unsigned int secure_key_size, + char *method, bool verbose); + ++int generate_key_verification_pattern(const char *key, size_t key_size, ++ char *vp, size_t vp_len, bool verbose); ++ + #endif +--- /dev/null ++++ b/zkey/zkey-cryptsetup.c +@@ -0,0 +1,2270 @@ ++/* ++ * zkey-cryptsetup - Re-encipher or validate volume keys of volumes ++ * encrypted with LUKS2 and the paes cipher. ++ * ++ * Copyright IBM Corp. 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. ++ */ ++ ++#define _LARGEFILE64_SOURCE ++ ++#include ++#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 "misc.h" ++#include "pkey.h" ++ ++#define MAX_KEY_SIZE (8 * 1024 * 1024) ++#define MAX_PASSWORD_SIZE 512 ++#define KEYFILE_BUFLEN 4096 ++#define SEEK_BUFLEN 4096 ++ ++#define PAES_VP_TOKEN_NAME "paes-verification-pattern" ++#define PAES_VP_TOKEN_VP "verification-pattern" ++ ++#define PAES_REENC_TOKEN_NAME "paes-reencipher" ++#define PAES_REENC_TOKEN_VP "verification-pattern" ++#define PAES_REENC_TOKEN_ORG_SLOT "original-keyslot" ++#define PAES_REENC_TOKEN_UNB_SLOT "unbound-keyslot" ++ ++struct reencipher_token { ++ char verification_pattern[VERIFICATION_PATTERN_LEN]; ++ unsigned int original_keyslot; ++ unsigned int unbound_keyslot; ++}; ++ ++struct vp_token { ++ char verification_pattern[VERIFICATION_PATTERN_LEN]; ++}; ++ ++__attribute__ ((unused)) ++static void misc_print_required_parms(const char *parm_name1, ++ const char *parm_name2); ++ ++/* ++ * Program configuration ++ */ ++const struct util_prg prg = { ++ .desc = "Manage secure volume keys of volumes encrypted with LUKS2 and " ++ "the 'paes' cipher", ++ .command_args = "COMMAND DEVICE", ++ .args = "", ++ .copyright_vec = { ++ { ++ .owner = "IBM Corp.", ++ .pub_first = 2018, ++ .pub_last = 2018, ++ }, ++ UTIL_PRG_COPYRIGHT_END ++ } ++}; ++ ++/* ++ * Global variables for program options ++ */ ++static struct zkey_cryptsetup_globals { ++ char *pos_arg; ++ char *keyfile; ++ long long keyfile_offset; ++ long long keyfile_size; ++ long long tries; ++ bool complete; ++ bool inplace; ++ bool staged; ++ char *master_key_file; ++ bool debug; ++ bool verbose; ++ void *lib_csulcca; ++ t_CSNBKTC dll_CSNBKTC; ++ int pkey_fd; ++ struct crypt_device *cd; ++} g = { ++ .tries = 3, ++ .pkey_fd = -1, ++}; ++ ++/* ++ * Available commands ++ */ ++#define COMMAND_REENCIPHER "reencipher" ++#define COMMAND_VALIDATE "validate" ++#define COMMAND_SETVP "setvp" ++#define COMMAND_SETKEY "setkey" ++ ++#define ZKEY_CRYPTSETUP_COMMAND_MAX_LEN 10 ++ ++/* ++ * These options are exactly the same as for the cryptsetup tool ++ */ ++#define OPT_PASSPHRASE_ENTRY(cmd) \ ++{ \ ++ .option = {"key-file", required_argument, NULL, 'd'}, \ ++ .argument = "FILE-NAME", \ ++ .desc = "Read the passphrase from the specified file", \ ++ .command = cmd, \ ++}, \ ++{ \ ++ .option = {"keyfile-offset", required_argument, NULL, 'o'}, \ ++ .argument = "BYTES", \ ++ .desc = "Specifies the number of bytes to skip in the file " \ ++ "specified with option '--key-file'|'-d'", \ ++ .command = cmd, \ ++}, \ ++{ \ ++ .option = {"keyfile-size", required_argument, NULL, 'l'}, \ ++ .argument = "BYTES", \ ++ .desc = "Specifies the number of bytes to read from the file " \ ++ "specified with option '--key-file'|'-d'", \ ++ .command = cmd, \ ++}, \ ++{ \ ++ .option = {"tries", required_argument, NULL, 'T'}, \ ++ .argument = "NUMBER", \ ++ .desc = "Specifies how often the interactive input of the " \ ++ "passphrase can be retried", \ ++ .command = cmd, \ ++} ++ ++/* ++ * Configuration of command line options ++ */ ++static struct util_opt opt_vec[] = { ++ /***********************************************************/ ++ { ++ .flags = UTIL_OPT_FLAG_SECTION, ++ .desc = "OPTIONS", ++ .command = COMMAND_REENCIPHER, ++ }, ++ { ++ .option = {"staged", 0, NULL, 's'}, ++ .desc = "Forces that the re-enciphering of a secure volume " ++ "key in the LUKS2 header is performed in staged mode", ++ .command = COMMAND_REENCIPHER, ++ }, ++ { ++ .option = {"in-place", 0, NULL, 'i'}, ++ .desc = "Forces an in-place re-enciphering of a secure volume " ++ "key in the LUKS2 header", ++ .command = COMMAND_REENCIPHER, ++ }, ++ { ++ .option = {"complete", 0, NULL, 'c'}, ++ .desc = "Completes a staged re-enciphering. Use this option " ++ "after the new CCA master key has been set (made " ++ "active)", ++ .command = COMMAND_REENCIPHER, ++ }, ++ OPT_PASSPHRASE_ENTRY(COMMAND_REENCIPHER), ++ /***********************************************************/ ++ { ++ .flags = UTIL_OPT_FLAG_SECTION, ++ .desc = "OPTIONS", ++ .command = COMMAND_VALIDATE, ++ }, ++ OPT_PASSPHRASE_ENTRY(COMMAND_VALIDATE), ++ /***********************************************************/ ++ { ++ .flags = UTIL_OPT_FLAG_SECTION, ++ .desc = "OPTIONS", ++ .command = COMMAND_SETVP, ++ }, ++ OPT_PASSPHRASE_ENTRY(COMMAND_SETVP), ++ /***********************************************************/ ++ { ++ .flags = UTIL_OPT_FLAG_SECTION, ++ .desc = "OPTIONS", ++ .command = COMMAND_SETKEY, ++ }, ++ { ++ .option = {"master-key-file", required_argument, NULL, 'm'}, ++ .argument = "FILE-NAME", ++ .desc = "Specifies the name of a file containing the secure " ++ "AES key that is set as new volume key", ++ .command = COMMAND_SETKEY, ++ }, ++ OPT_PASSPHRASE_ENTRY(COMMAND_SETKEY), ++ /***********************************************************/ ++ { ++ .flags = UTIL_OPT_FLAG_SECTION, ++ .desc = "COMMON OPTIONS" ++ }, ++ { ++ .option = {"debug", 0, NULL, 'D'}, ++ .desc = "Print additional debugging messages during " ++ "processing", ++ }, ++ { ++ .option = {"verbose", 0, NULL, 'V'}, ++ .desc = "Print additional information messages during " ++ "processing", ++ }, ++ UTIL_OPT_HELP, ++ UTIL_OPT_VERSION, ++ UTIL_OPT_END ++}; ++ ++#define ZKEY_CRYPTSETUP_COMMAND_STR_LEN 80 ++ ++/* ++ * Table of supported commands ++ */ ++struct zkey_cryptsetup_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 open_device; ++}; ++ ++static int command_reencipher(void); ++static int command_validate(void); ++static int command_setvp(void); ++static int command_setkey(void); ++ ++static struct zkey_cryptsetup_command zkey_cryptsetup_commands[] = { ++ { ++ .command = COMMAND_REENCIPHER, ++ .abbrev_len = 2, ++ .function = command_reencipher, ++ .need_cca_library = 1, ++ .need_pkey_device = 1, ++ .short_desc = "Re-encipher a secure volume key", ++ .long_desc = "Re-encipher a secure volume key of a volume " ++ "encrypted with LUKS2 and the 'paes' cipher", ++ .has_options = 1, ++ .pos_arg = "DEVICE", ++ .open_device = 1, ++ }, ++ { ++ .command = COMMAND_VALIDATE, ++ .abbrev_len = 3, ++ .function = command_validate, ++ .need_pkey_device = 1, ++ .short_desc = "Validate a secure volume key", ++ .long_desc = "Validate a secure volume key of a volume " ++ "encrypted with LUKS2 and the 'paes' cipher", ++ .has_options = 1, ++ .pos_arg = "DEVICE", ++ .open_device = 1, ++ }, ++ { ++ .command = COMMAND_SETVP, ++ .abbrev_len = 4, ++ .function = command_setvp, ++ .need_pkey_device = 1, ++ .short_desc = "Set a verification pattern of the secure volume " ++ "key", ++ .long_desc = "Set a verification pattern of the secure AES " ++ "volume key of a volume encrypted with LUKS2 and " ++ "the 'paes' cipher", ++ .has_options = 1, ++ .pos_arg = "DEVICE", ++ .open_device = 1, ++ }, ++ { ++ .command = COMMAND_SETKEY, ++ .abbrev_len = 4, ++ .function = command_setkey, ++ .need_pkey_device = 1, ++ .short_desc = "Set a new secure volume key", ++ .long_desc = "Set a new secure AES volume key for a volume " ++ "encrypted with LUKS2 and the 'paes' cipher", ++ .has_options = 1, ++ .pos_arg = "DEVICE", ++ .open_device = 1, ++ }, ++ { .command = NULL } ++}; ++ ++#define pr_verbose(fmt...) do { \ ++ if (g.verbose) \ ++ warnx(fmt); \ ++ } while (0) ++ ++static volatile int quit; ++ ++/* ++ * Signal handler for SIGINT and SIGTERM ++ */ ++static void int_handler(int sig __attribute__((__unused__))) ++{ ++ quit++; ++} ++ ++/* ++ * Install signal handler for SIGINT and SIGTERM ++ */ ++static void set_int_handler(void) ++{ ++ struct sigaction sigaction_open; ++ ++ pr_verbose("Installing SIGINT/SIGTERM handler"); ++ memset(&sigaction_open, 0, sizeof(struct sigaction)); ++ sigaction_open.sa_handler = int_handler; ++ sigaction(SIGINT, &sigaction_open, 0); ++ sigaction(SIGTERM, &sigaction_open, 0); ++} ++ ++static void print_usage_command(const struct zkey_cryptsetup_command *command) ++{ ++ char command_str[ZKEY_CRYPTSETUP_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_cryptsetup_command *cmd = zkey_cryptsetup_commands; ++ char command_str[ZKEY_CRYPTSETUP_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_CRYPTSETUP_COMMAND_MAX_LEN, ++ command_str, cmd->short_desc); ++ cmd++; ++ } ++ printf("\n"); ++} ++ ++/* ++ * --help printout ++ */ ++static void print_help(const struct zkey_cryptsetup_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); ++ } ++} ++ ++/* ++ * Log function called from libcryptsetup routines when debugging is enabled ++ */ ++static void cryptsetup_log(int level, const char *msg, ++ void *usrptr __attribute__((unused))) ++{ ++ switch (level) { ++ case CRYPT_LOG_NORMAL: ++ fputs(msg, stdout); ++ break; ++ case CRYPT_LOG_VERBOSE: ++ if (g.verbose) ++ fputs(msg, stdout); ++ break; ++ case CRYPT_LOG_ERROR: ++ fprintf(stderr, "%s: %s", program_invocation_short_name, msg); ++ break; ++ case CRYPT_LOG_DEBUG: ++ fprintf(stderr, "%s: # %s", program_invocation_short_name, msg); ++ break; ++ default: ++ warnx("Internal error on logging class for msg: %s", msg); ++ break; ++ } ++} ++ ++static void secure_free(void *area, size_t size) ++{ ++ if (area == NULL) ++ return; ++ ++ memset(area, 0, size); ++ free(area); ++} ++ ++/* ++ * Seek a number of bytes in a file. ++ * ++ * A simple call to lseek(3) might not be possible for some inputs (e.g. ++ * reading from a pipe), so this function instead reads of up to 4K bytes ++ * at a time until the specified number of bytes. It returns -1 on read error ++ * or when it reaches EOF before the requested number of bytes have been ++ * discarded. ++ */ ++static int keyfile_seek(int fd, size_t bytes) ++{ ++ size_t next_read; ++ ssize_t bytes_r; ++ off64_t r; ++ char *tmp; ++ ++ r = lseek64(fd, bytes, SEEK_CUR); ++ if (r > 0) ++ return 0; ++ if (r < 0 && errno != ESPIPE) ++ return -1; ++ ++ tmp = util_malloc(SEEK_BUFLEN); ++ while (bytes > 0) { ++ next_read = bytes > SEEK_BUFLEN ? SEEK_BUFLEN : (size_t)bytes; ++ ++ bytes_r = read(fd, tmp, next_read); ++ if (bytes_r < 0) { ++ if (errno == EINTR) ++ continue; ++ secure_free(tmp, SEEK_BUFLEN); ++ return -1; ++ } ++ ++ if (bytes_r == 0) ++ break; ++ ++ bytes -= bytes_r; ++ } ++ ++ secure_free(tmp, SEEK_BUFLEN); ++ return bytes == 0 ? 0 : -1; ++} ++ ++/* ++ * Read data from fd into the specified buffer ++ */ ++static ssize_t keyfile_read(int fd, void *buf, size_t length) ++{ ++ size_t read_size = 0; ++ ssize_t r; ++ ++ if (fd < 0 || buf == NULL) ++ return -EINVAL; ++ ++ do { ++ r = read(fd, buf, length - read_size); ++ if (r == -1 && errno != EINTR) ++ return r; ++ if (r == 0) ++ return (ssize_t)read_size; ++ if (r > 0) { ++ read_size += (size_t)r; ++ buf = (char *)buf + r; ++ } ++ } while (read_size != length); ++ ++ return (ssize_t)length; ++} ++ ++/* ++ * Prompt for the password ++ */ ++static int get_password_interactive(const char *prompt, char **pwd, ++ size_t *pwd_size) ++{ ++ struct termios orig, tmp; ++ int infd, outfd, rc = 0; ++ char *pass; ++ int num; ++ ++ pass = calloc(MAX_PASSWORD_SIZE + 1, 1); ++ if (pass == NULL) { ++ warnx("Out of memory while reading passphrase"); ++ return -ENOMEM; ++ } ++ ++ infd = open("/dev/tty", O_RDWR); ++ if (infd == -1) { ++ infd = STDIN_FILENO; ++ outfd = STDERR_FILENO; ++ } else { ++ outfd = infd; ++ } ++ ++ if (prompt != NULL) { ++ if (write(outfd, prompt, strlen(prompt)) < 0) { ++ rc = -errno; ++ warnx("Failed to write prompt: %s", strerror(-rc)); ++ goto out_err; ++ } ++ } ++ ++ rc = tcgetattr(infd, &orig); ++ if (rc != 0) { ++ rc = -errno; ++ warnx("Failed to get terminal attributes: %s", strerror(-rc)); ++ goto out_err; ++ } ++ ++ memcpy(&tmp, &orig, sizeof(tmp)); ++ tmp.c_lflag &= ~ECHO; ++ ++ rc = tcsetattr(infd, TCSAFLUSH, &tmp); ++ if (rc != 0) { ++ rc = -errno; ++ warnx("Failed to set terminal attributes: %s", strerror(-rc)); ++ goto out_err; ++ } ++ ++ quit = 0; ++ num = read(infd, pass, MAX_PASSWORD_SIZE); ++ if (num > 0) ++ pass[num - 1] = '\0'; ++ else if (num == 0) ++ *pass = '\0'; ++ ++ if (quit) { ++ printf("\n"); ++ num = -1; ++ pr_verbose("Password entry aborted by user"); ++ } ++ ++ rc = tcsetattr(infd, TCSAFLUSH, &orig); ++ if (rc != 0) { ++ rc = -errno; ++ warnx("Failed to set terminal attributes: %s", strerror(-rc)); ++ goto out_err; ++ } ++ ++ if (num < 0) { ++ warnx("Failed to read the password"); ++ rc = -EIO; ++ goto out_err; ++ } ++ ++ *pwd = pass; ++ *pwd_size = strlen(pass); ++ rc = 0; ++ ++out_err: ++ if (rc != 0) ++ secure_free(pass, MAX_PASSWORD_SIZE + 1); ++ else ++ write(outfd, "\n", 1); ++ ++ if (infd != STDIN_FILENO) ++ close(infd); ++ ++ return rc; ++} ++ ++/* ++ * Read the password from the key file ++ */ ++static int get_password_file(char **pwd, size_t *pwd_size, const char *key_file, ++ size_t keyfile_offset, size_t key_size, ++ int stop_at_eol) ++{ ++ int unlimited_read = 0; ++ size_t file_read_size; ++ int regular_file = 0; ++ int char_to_read = 0; ++ int fd, rc, newline; ++ char *pass = NULL; ++ int char_read = 0; ++ size_t buflen, i; ++ struct stat sb; ++ ++ fd = key_file ? open(key_file, O_RDONLY) : STDIN_FILENO; ++ if (fd < 0) { ++ rc = -errno; ++ warnx("Failed to open key file '%s': %s", key_file, ++ strerror(-rc)); ++ return rc; ++ } ++ ++ if (isatty(fd)) { ++ warnx("Cannot read key file from a terminal"); ++ rc = -EINVAL; ++ goto out_err; ++ } ++ ++ if (key_size == 0) { ++ key_size = MAX_KEY_SIZE + 1; ++ unlimited_read = 1; ++ buflen = KEYFILE_BUFLEN; ++ } else ++ buflen = key_size; ++ ++ if (key_file) { ++ rc = stat(key_file, &sb); ++ if (rc != 0) { ++ warnx("Failed to stat key file '%s': %s", key_file, ++ strerror(-rc)); ++ goto out_err; ++ } ++ if (S_ISREG(sb.st_mode)) { ++ regular_file = 1; ++ file_read_size = sb.st_size; ++ ++ if (keyfile_offset > file_read_size) { ++ warnx("Cannot seek to requested key file " ++ "offset %lu", keyfile_offset); ++ goto out_err; ++ } ++ file_read_size -= keyfile_offset; ++ ++ if (file_read_size >= key_size) ++ buflen = key_size; ++ else if (file_read_size) ++ buflen = file_read_size; ++ } ++ } ++ ++ pass = calloc(buflen, 1); ++ if (pass == NULL) { ++ warnx("Out of memory while reading passphrase"); ++ rc = -ENOMEM; ++ goto out_err; ++ } ++ ++ if (keyfile_offset && keyfile_seek(fd, keyfile_offset) < 0) { ++ warnx("Cannot seek to requested key file offset %lu", ++ keyfile_offset); ++ goto out_err; ++ } ++ ++ for (i = 0, newline = 0; i < key_size; i += char_read) { ++ if (i == buflen) { ++ buflen += 4096; ++ pass = realloc(pass, buflen); ++ if (pass == NULL) { ++ warnx("Out of memory while reading passphrase"); ++ rc = -ENOMEM; ++ goto out_err; ++ } ++ } ++ ++ if (stop_at_eol) ++ char_to_read = 1; ++ else ++ char_to_read = key_size < buflen ? ++ key_size - i : buflen - i; ++ ++ char_read = keyfile_read(fd, &pass[i], char_to_read); ++ if (char_read < 0) { ++ warnx("Error reading passphrase"); ++ rc = -EPIPE; ++ goto out_err; ++ } ++ ++ if (char_read == 0) ++ break; ++ ++ if (stop_at_eol && pass[i] == '\n') { ++ newline = 1; ++ pass[i] = '\0'; ++ break; ++ } ++ } ++ ++ if (!i && !regular_file && !newline) { ++ warnx("Nothing read on input"); ++ rc = -EPIPE; ++ goto out_err; ++ } ++ ++ if (unlimited_read && i == key_size) { ++ warnx("Maximum key size exceeded"); ++ rc = -EINVAL; ++ goto out_err; ++ } ++ ++ if (!unlimited_read && i != key_size) { ++ warnx("Cannot read requested amount of data"); ++ rc = -EINVAL; ++ goto out_err; ++ } ++ ++ *pwd = pass; ++ *pwd_size = i; ++ rc = 0; ++ ++out_err: ++ if (fd != STDIN_FILENO) ++ close(fd); ++ if (rc != 0) ++ secure_free(pass, buflen); ++ ++ return rc; ++} ++ ++/* ++ * Check if the specfied file name denotes stdin ++ */ ++static bool is_stdin(const char *file_name) ++{ ++ if (file_name == NULL) ++ return true; ++ ++ return strcmp(file_name, "-") ? false : true; ++} ++ ++/* ++ * Prompt for the password or read the password from the keyfile. ++ */ ++static int get_password(const char *prompt, char **pwd, size_t *pwd_size, ++ const char *key_file, size_t keyfile_offset, ++ size_t keyfile_size) ++{ ++ int rc; ++ ++ if (is_stdin(key_file)) { ++ if (isatty(STDIN_FILENO)) { ++ if (keyfile_offset) { ++ warnx("Cannot use option --keyfile-offset with " ++ "terminal input"); ++ return -EINVAL; ++ } ++ if (keyfile_size) { ++ warnx("Cannot use option --keyfile-size with " ++ "terminal input"); ++ return -EINVAL; ++ } ++ ++ rc = get_password_interactive(prompt, pwd, pwd_size); ++ } else { ++ rc = get_password_file(pwd, pwd_size, NULL, ++ keyfile_offset, keyfile_size, ++ key_file == NULL); ++ } ++ } else { ++ rc = get_password_file(pwd, pwd_size, key_file, ++ keyfile_offset, keyfile_size, 0); ++ } ++ ++ return rc; ++} ++static int ensure_is_active_keylot(int keyslot) ++{ ++ crypt_keyslot_info info; ++ ++ info = crypt_keyslot_status(g.cd, keyslot); ++ if (info != CRYPT_SLOT_ACTIVE && info != CRYPT_SLOT_ACTIVE_LAST) { ++ warnx("Keyslot %d is not a valid key slot", keyslot); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ensure_is_unbound_keylot(int keyslot) ++{ ++ crypt_keyslot_info info; ++ ++ info = crypt_keyslot_status(g.cd, keyslot); ++ if (info != CRYPT_SLOT_UNBOUND) { ++ warnx("Key slot %d is not an unbound key slot", keyslot); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Returns the token number of the token of the specified name if found, ++ * -1 otherwise. ++ */ ++static int find_token(struct crypt_device *cd, const char *name) ++{ ++ crypt_token_info info; ++ const char *type; ++ int i; ++ ++ for (i = 0; ; i++) { ++ info = crypt_token_status(cd, i, &type); ++ if (info == CRYPT_TOKEN_INVALID) ++ break; ++ if (info == CRYPT_TOKEN_INACTIVE) ++ continue; ++ ++ if (strcmp(type, name) != 0) ++ continue; ++ ++ pr_verbose("'%s' token found at slot %d", name, i); ++ return i; ++ } ++ ++ pr_verbose("'%s' token not found", name); ++ return -1; ++} ++ ++/* ++ * Validate the reencipher token ++ */ ++static int validate_reencipher_token(struct reencipher_token *tok) ++{ ++ int rc; ++ ++ rc = ensure_is_unbound_keylot(tok->unbound_keyslot); ++ if (rc != 0) ++ return rc; ++ ++ rc = ensure_is_active_keylot(tok->original_keyslot); ++ if (rc != 0) ++ return rc; ++ ++ pr_verbose("The re-encipher token has been validated"); ++ ++ return 0; ++} ++ ++static int get_token(struct crypt_device *cd, int token, json_object **obj) ++{ ++ const char *json; ++ int rc; ++ ++ if (obj == NULL) ++ return -EINVAL; ++ ++ rc = crypt_token_json_get(cd, token, &json); ++ if (rc < 0) { ++ warnx("Failed to get re-encipher token %d: %s", token, ++ strerror(-rc)); ++ return -rc; ++ } ++ ++ *obj = json_tokener_parse(json); ++ if (*obj == NULL) { ++ warnx("Failed to parse JSON"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Reads the re-encipher token from the LUKS2 header ++ */ ++static int get_reencipher_token(struct crypt_device *cd, int token, ++ struct reencipher_token *info, bool validate) ++{ ++ json_object *jobj_org_keyslot = NULL; ++ json_object *jobj_unb_keyslot = NULL; ++ json_object *json_token = NULL; ++ json_object *jobj_vp = NULL; ++ const char *temp; ++ int rc; ++ ++ rc = get_token(cd, token, &json_token); ++ if (rc != 0) ++ return rc; ++ ++ if (!json_object_object_get_ex(json_token, PAES_REENC_TOKEN_VP, ++ &jobj_vp)) { ++ warnx("The re-encipher token is incomplete, '%s' is missing", ++ PAES_REENC_TOKEN_VP); ++ rc = -EINVAL; ++ goto out; ++ } ++ temp = json_object_get_string(jobj_vp); ++ if (temp == NULL) { ++ warnx("The re-encipher token is incomplete, '%s' is missing", ++ PAES_REENC_TOKEN_VP); ++ rc = -EINVAL; ++ goto out; ++ } ++ strncpy(info->verification_pattern, temp, ++ sizeof(info->verification_pattern)); ++ info->verification_pattern[ ++ sizeof(info->verification_pattern) - 1] = '\0'; ++ ++ if (!json_object_object_get_ex(json_token, PAES_REENC_TOKEN_ORG_SLOT, ++ &jobj_org_keyslot)) { ++ warnx("The re-encipher token is incomplete, '%s' is missing", ++ PAES_REENC_TOKEN_ORG_SLOT); ++ rc = -EINVAL; ++ goto out; ++ } ++ errno = 0; ++ info->original_keyslot = json_object_get_int64(jobj_org_keyslot); ++ if (errno != 0) { ++ warnx("The re-encipher token is incomplete, '%s' is missing", ++ PAES_REENC_TOKEN_ORG_SLOT); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ if (!json_object_object_get_ex(json_token, PAES_REENC_TOKEN_UNB_SLOT, ++ &jobj_unb_keyslot)) { ++ warnx("The re-encipher token is incomplete, '%s' is missing", ++ PAES_REENC_TOKEN_UNB_SLOT); ++ rc = -EINVAL; ++ goto out; ++ } ++ errno = 0; ++ info->unbound_keyslot = json_object_get_int64(jobj_unb_keyslot); ++ if (errno != 0) { ++ warnx("The re-encipher token is incomplete, '%s' is missing", ++ PAES_REENC_TOKEN_UNB_SLOT); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ pr_verbose("Re-encipher token: original-keyslot: %d, unbound-keyslot: " ++ "%d, verification-pattern: %s", info->original_keyslot, ++ info->unbound_keyslot, info->verification_pattern); ++ ++ rc = 0; ++ ++ if (validate) ++ rc = validate_reencipher_token(info); ++ ++out: ++ if (json_token != NULL) ++ json_object_put(json_token); ++ ++ return rc; ++} ++ ++/* ++ * Writes the re-encipher token to the LUKS2 header ++ */ ++static int put_reencipher_token(struct crypt_device *cd, int token, ++ struct reencipher_token *info) ++{ ++ json_object *jobj, *jobj_keyslots; ++ char temp[20]; ++ int rc; ++ ++ pr_verbose("Re-encipher token: original-keyslot: %d, unbound-keyslot: " ++ "%d, verification-pattern: %s", info->original_keyslot, ++ info->unbound_keyslot, info->verification_pattern); ++ ++ jobj = json_object_new_object(); ++ json_object_object_add(jobj, "type", ++ json_object_new_string(PAES_REENC_TOKEN_NAME)); ++ ++ jobj_keyslots = json_object_new_array(); ++ sprintf(temp, "%d", info->unbound_keyslot); ++ json_object_array_add(jobj_keyslots, json_object_new_string(temp)); ++ json_object_object_add(jobj, "keyslots", jobj_keyslots); ++ ++ json_object_object_add(jobj, PAES_REENC_TOKEN_VP, ++ json_object_new_string( ++ info->verification_pattern)); ++ json_object_object_add(jobj, PAES_REENC_TOKEN_ORG_SLOT, ++ json_object_new_int64(info->original_keyslot)); ++ json_object_object_add(jobj, PAES_REENC_TOKEN_UNB_SLOT, ++ json_object_new_int64(info->unbound_keyslot)); ++ ++ rc = crypt_token_json_set(cd, token >= 0 ? token : CRYPT_ANY_TOKEN, ++ json_object_to_json_string_ext(jobj, ++ JSON_C_TO_STRING_PLAIN)); ++ ++ if (rc < 0) ++ warnx("Failed to add the re-encipher token to device " ++ "'%s': %s", g.pos_arg, strerror(-rc)); ++ else ++ pr_verbose("Re-encipher token put to token slot %d", ++ rc); ++ ++ json_object_put(jobj); ++ ++ return rc; ++} ++ ++ ++/* ++ * Reads the verification pattern token from the LUKS2 header ++ */ ++static int get_vp_token(struct crypt_device *cd, int token, ++ struct vp_token *info) ++{ ++ json_object *json_token = NULL; ++ json_object *jobj_vp = NULL; ++ const char *temp; ++ int rc; ++ ++ rc = get_token(cd, token, &json_token); ++ if (rc != 0) ++ return rc; ++ ++ if (!json_object_object_get_ex(json_token, PAES_VP_TOKEN_VP, ++ &jobj_vp)) { ++ warnx("The verification-pattern token is incomplete, '%s' is " ++ "missing", PAES_VP_TOKEN_VP); ++ rc = -EINVAL; ++ goto out; ++ } ++ temp = json_object_get_string(jobj_vp); ++ if (temp == NULL) { ++ warnx("The verification-pattern token is incomplete, '%s' is " ++ "missing", PAES_VP_TOKEN_VP); ++ rc = -EINVAL; ++ goto out; ++ } ++ strncpy(info->verification_pattern, temp, ++ sizeof(info->verification_pattern)); ++ info->verification_pattern[ ++ sizeof(info->verification_pattern) - 1] = '\0'; ++ ++ pr_verbose("Verification-pattern: %s", info->verification_pattern); ++ ++out: ++ if (json_token != NULL) ++ json_object_put(json_token); ++ ++ return rc; ++} ++ ++/* ++ * Writes the verification pattern token to the LUKS2 header ++ */ ++static int put_vp_token(struct crypt_device *cd, int token, ++ struct vp_token *info) ++{ ++ json_object *jobj, *jobj_keyslots; ++ int rc; ++ ++ pr_verbose("Verification-pattern: %s", info->verification_pattern); ++ ++ jobj = json_object_new_object(); ++ json_object_object_add(jobj, "type", ++ json_object_new_string(PAES_VP_TOKEN_NAME)); ++ ++ jobj_keyslots = json_object_new_array(); ++ json_object_object_add(jobj, "keyslots", jobj_keyslots); ++ ++ json_object_object_add(jobj, PAES_VP_TOKEN_VP, ++ json_object_new_string( ++ info->verification_pattern)); ++ ++ rc = crypt_token_json_set(cd, token >= 0 ? token : CRYPT_ANY_TOKEN, ++ json_object_to_json_string_ext(jobj, ++ JSON_C_TO_STRING_PLAIN)); ++ ++ if (rc < 0) ++ warnx("Failed to add the verification-pattern token to device " ++ "'%s': %s", g.pos_arg, strerror(-rc)); ++ else ++ pr_verbose("Verification-pattern token put to token slot %d", ++ rc); ++ ++ json_object_put(jobj); ++ ++ return rc; ++} ++ ++/* ++ * Open the LUKS2 device ++ */ ++static int open_device(const char *device, struct crypt_device **cd) ++{ ++ const struct crypt_pbkdf_type *pbkdf; ++ struct crypt_device *cdev = NULL; ++ int rc; ++ ++ rc = crypt_init(&cdev, device); ++ if (rc != 0) { ++ warnx("Failed to open device '%s': %s", device, strerror(-rc)); ++ goto out; ++ } ++ ++ crypt_set_log_callback(cdev, cryptsetup_log, NULL); ++ ++ rc = crypt_load(cdev, CRYPT_LUKS, NULL); ++ if (rc != 0) { ++ warnx("Failed to load the header from device '%s': %s", device, ++ strerror(-rc)); ++ goto out; ++ } ++ ++ if (strcmp(crypt_get_type(cdev), CRYPT_LUKS2) != 0) { ++ warnx("Device '%s' is not a LUKS2 device", device); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ if (strcmp(crypt_get_cipher(cdev), "paes") != 0) { ++ warnx("Device '%s' is not encrypted using the 'paes' cipher", ++ device); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ pbkdf = crypt_get_pbkdf_type(cdev); ++ rc = crypt_set_pbkdf_type(cdev, pbkdf); ++ if (rc != 0) { ++ warnx("Failed to set the PBKDF-type for device '%s': %s", ++ device, strerror(-rc)); ++ goto out; ++ } ++ ++ *cd = cdev; ++ ++out: ++ if (rc != 0) { ++ if (cdev != NULL) ++ crypt_free(cdev); ++ *cd = NULL; ++ } ++ ++ return rc; ++} ++ ++/* ++ * Prompts for yes or no. Returns true if 'y' or 'yes' was entered. ++ */ ++static bool prompt_for_yes(void) ++{ ++ char str[20]; ++ ++ if (fgets(str, sizeof(str), stdin) == NULL) ++ return false; ++ ++ if (str[strlen(str) - 1] == '\n') ++ str[strlen(str) - 1] = '\0'; ++ pr_verbose("Prompt reply: '%s'", str); ++ if (strcasecmp(str, "y") == 0 || strcasecmp(str, "yes") == 0) ++ return true; ++ ++ return false; ++} ++ ++/* ++ * Cleans up a left over re-encipher token and associated unbound keyslot ++ */ ++static int cleanup_reencipher_token(int token) ++{ ++ struct reencipher_token tok; ++ int rc; ++ ++ rc = get_reencipher_token(g.cd, token, &tok, false); ++ if (rc == 0) { ++ if (ensure_is_unbound_keylot(tok.unbound_keyslot) == 0) { ++ rc = crypt_keyslot_destroy(g.cd, tok.unbound_keyslot); ++ if (rc != 0) ++ pr_verbose("Failed to destroy unbound key slot " ++ "%d: %s", tok.unbound_keyslot, ++ strerror(-rc)); ++ else ++ pr_verbose("Successfully destroyed unbound key " ++ "slot %d", tok.unbound_keyslot); ++ } else { ++ pr_verbose("Key slot %d is not in unbound state, it is " ++ "not destroyed", tok.unbound_keyslot); ++ } ++ } else { ++ pr_verbose("Failed to get re-encipher token (ignored): %s", ++ strerror(-rc)); ++ } ++ ++ rc = crypt_token_json_set(g.cd, token, NULL); ++ if (rc < 0) ++ warnx("Failed to remove the re-encipher token: %s", ++ strerror(-rc)); ++ else ++ pr_verbose("Successfully removed re-encipher token %d", token); ++ ++ return rc; ++} ++ ++/* ++ * Activates an unbound key slot and removes the previous key slots ++ */ ++static int activate_unbound_keyslot(int token, int keyslot, const char *key, ++ size_t keysize, char *password, ++ size_t password_len, char *complete_msg) ++{ ++ crypt_keyslot_info info; ++ int rc, i, n; ++ ++ rc = crypt_keyslot_add_by_key(g.cd, keyslot, key, keysize, password, ++ password_len, CRYPT_VOLUME_KEY_SET); ++ if (rc < 0) { ++ warnx("Failed to activate the unbound key slot %d: %s", keyslot, ++ strerror(-rc)); ++ return rc; ++ } ++ ++ pr_verbose("Unbound key slot %d activated, it is now key slot %d", ++ keyslot, rc); ++ keyslot = rc; ++ ++ if (token >= 0) { ++ rc = crypt_token_json_set(g.cd, token, NULL); ++ if (rc < 0) { ++ warnx("Failed remove the re-encipher token %d: %s", ++ token, strerror(-rc)); ++ return rc; ++ } ++ } ++ ++ if (complete_msg != NULL) ++ util_print_indented(complete_msg, 0); ++ util_print_indented("All key slots containing the old volume key are " ++ "now in unbound state. Do you want to remove " ++ "these key slots?", 0); ++ ++ if (!prompt_for_yes()) ++ return 0; ++ ++ for (i = 0, n = 0; ; i++) { ++ if (i == keyslot) ++ continue; ++ ++ info = crypt_keyslot_status(g.cd, i); ++ if (info == CRYPT_SLOT_INVALID) ++ break; ++ if (info <= CRYPT_SLOT_ACTIVE_LAST) ++ continue; ++ ++ pr_verbose("Removing now unbound key slot %d", i); ++ rc = crypt_keyslot_destroy(g.cd, i); ++ if (rc < 0) { ++ warnx("Failed to remove previous key slot %d: %s", i, ++ strerror(-rc)); ++ } ++ ++ n++; ++ } ++ ++ if (n > 1) { ++ util_print_indented("\nWARNING: Before re-enciphering, the " ++ "volume's LUKS header had multiple active " ++ "key slots with the same key, but different " ++ "passwords. Use 'cryptsetup luksAddKey' if " ++ "you need more than one key slot.", 0); ++ } ++ ++ return rc; ++} ++ ++static int check_keysize_and_cipher_mode(size_t keysize) ++{ ++ if (keysize == 0) { ++ warnx("Invalid volume key size"); ++ return -EINVAL; ++ } ++ ++ if (strncmp(crypt_get_cipher_mode(g.cd), "xts", 3) == 0) { ++ if (keysize != 2 * SECURE_KEY_SIZE) { ++ warnx("The volume key size %lu is not valid for the " ++ "cipher mode '%s'", keysize, ++ crypt_get_cipher_mode(g.cd)); ++ return -EINVAL; ++ } ++ } else { ++ if (keysize != SECURE_KEY_SIZE) { ++ warnx("The volume key size %lu is not valid for the " ++ "cipher mode '%s'", keysize, ++ crypt_get_cipher_mode(g.cd)); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Open a keyslot and get a secure key from a key slot. Optionally returns the ++ * key and password used to unlock the keyslot. You can either open a specific ++ * key slot, or let it choose based on the password (keyslot=CRYPT_ANY_SLOT). ++ */ ++static int open_keyslot(int keyslot, char **key, size_t *keysize, ++ char **password, size_t *password_len, ++ const char *prompt) ++{ ++ char *vkey = NULL; ++ char *pw = NULL; ++ long long tries; ++ size_t vkeysize; ++ size_t pw_len; ++ int rc; ++ ++ vkeysize = crypt_get_volume_key_size(g.cd); ++ pr_verbose("Volume key size: %lu", vkeysize); ++ ++ rc = check_keysize_and_cipher_mode(vkeysize); ++ if (rc != 0) ++ return rc; ++ ++ vkey = malloc(vkeysize); ++ if (vkey == NULL) { ++ warnx("Out of memory while allocating a buffer for the volume " ++ "key"); ++ return -ENOMEM; ++ } ++ ++ tries = (is_stdin(g.keyfile) && isatty(STDIN_FILENO)) ? g.tries : 1; ++ do { ++ if (pw != NULL) { ++ secure_free(pw, pw_len); ++ pw = NULL; ++ } ++ ++ rc = get_password(prompt, &pw, &pw_len, g.keyfile, ++ g.keyfile_offset, g.keyfile_size); ++ if (rc != 0) ++ goto out; ++ ++ rc = crypt_volume_key_get(g.cd, keyslot, vkey, &vkeysize, ++ pw, pw_len); ++ ++ if (rc == -EPERM || rc == -ENOENT) ++ warnx("No key available with this passphrase"); ++ ++ ++ } while ((rc == -EPERM || rc == -ENOENT) && (--tries > 0)); ++ ++ if (rc < 0) { ++ warnx("Failed to get volume key of device '%s': " ++ "%s", g.pos_arg, strerror(-rc)); ++ goto out; ++ } ++ ++ keyslot = rc; ++ pr_verbose("Volume key obtained from key slot %d", keyslot); ++ ++ if (key != NULL) ++ *key = vkey; ++ else ++ secure_free(vkey, vkeysize); ++ vkey = NULL; ++ if (keysize != NULL) ++ *keysize = vkeysize; ++ if (password != NULL) ++ *password = pw; ++ else ++ secure_free(pw, pw_len); ++ pw = NULL; ++ if (password_len != NULL) ++ *password_len = pw_len; ++ ++ rc = keyslot; ++ ++out: ++ secure_free(vkey, vkeysize); ++ secure_free(pw, pw_len); ++ ++ return rc; ++} ++ ++ ++/* ++ * Validate and get a secure key from a key slot. Optionally returns the key ++ * and password used to unlock the keyslot. You can either validate a specific ++ * key slot, or let it choose based on the password (keyslot=CRYPT_ANY_SLOT). ++ */ ++static int validate_keyslot(int keyslot, char **key, size_t *keysize, ++ char **password, size_t *password_len, ++ int *is_old_mk, size_t *clear_keysize, ++ const char *prompt, const char *invalid_msg) ++{ ++ size_t vkeysize = 0; ++ char *vkey = NULL; ++ int rc, is_old; ++ ++ rc = open_keyslot(keyslot, &vkey, &vkeysize, password, password_len, ++ prompt); ++ if (rc < 0) ++ return rc; ++ ++ keyslot = rc; ++ ++ rc = validate_secure_key(g.pkey_fd, (u8 *)vkey, vkeysize, clear_keysize, ++ &is_old, g.verbose); ++ if (rc != 0) { ++ if (invalid_msg != NULL) ++ warnx("%s", invalid_msg); ++ else ++ warnx("The secure volume key of device '%s' is not " ++ "valid", g.pos_arg); ++ rc = -EINVAL; ++ goto out; ++ } ++ pr_verbose("Volume key is currently enciphered with %s master key", ++ is_old ? "OLD" : "CURRENT"); ++ ++ if (key != NULL) ++ *key = vkey; ++ else ++ secure_free(vkey, vkeysize); ++ vkey = NULL; ++ if (keysize != NULL) ++ *keysize = vkeysize; ++ if (is_old_mk != NULL) ++ *is_old_mk = is_old; ++ ++ rc = keyslot; ++ ++out: ++ secure_free(vkey, vkeysize); ++ ++ return rc; ++} ++ ++/* ++ * Prepares for a re-enciphering of a secure volume key. Dependent on the ++ * options specified by the user and the state of the volume key, it starts ++ * a staged re-enciphering or performs an in-place re-enciphering. ++ */ ++static int reencipher_prepare(int token) ++{ ++ struct reencipher_token reenc_tok; ++ struct vp_token vp_tok; ++ char *password = NULL; ++ size_t password_len; ++ char *key = NULL; ++ size_t keysize; ++ int is_old_mk; ++ char *prompt; ++ char *msg; ++ int rc; ++ ++ if (token >= 0) { ++ util_asprintf(&msg, "Staged volume key re-enciphering is " ++ "already initiated for device '%s'. Do you want to " ++ "cancel the pending re-enciphering and start a " ++ "new re-enciphering process?", g.pos_arg); ++ util_print_indented(msg, 0); ++ free(msg); ++ ++ if (!prompt_for_yes()) { ++ warnx("Device '%s' is left unchanged", g.pos_arg); ++ return -ECANCELED; ++ } ++ ++ rc = cleanup_reencipher_token(token); ++ if (rc < 0) ++ return rc; ++ } ++ ++ util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg); ++ rc = validate_keyslot(CRYPT_ANY_SLOT, &key, &keysize, &password, ++ &password_len, &is_old_mk, NULL, prompt, NULL); ++ free(prompt); ++ if (rc < 0) ++ goto out; ++ ++ reenc_tok.original_keyslot = rc; ++ ++ rc = ensure_is_active_keylot(reenc_tok.original_keyslot); ++ if (rc != 0) ++ goto out; ++ ++ rc = generate_key_verification_pattern(key, keysize, ++ reenc_tok.verification_pattern, ++ sizeof(reenc_tok.verification_pattern), ++ 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"); ++ goto out; ++ } ++ ++ memcpy(vp_tok.verification_pattern, reenc_tok.verification_pattern, ++ sizeof(vp_tok.verification_pattern)); ++ token = find_token(g.cd, PAES_VP_TOKEN_NAME); ++ rc = put_vp_token(g.cd, token, &vp_tok); ++ if (rc < 0) ++ goto out; ++ ++ util_asprintf(&msg, "The secure volume key of device '%s' is " ++ "enciphered with the %s CCA master key and is being " ++ "re-enciphered with the %s CCA master key.", ++ g.pos_arg, is_old_mk ? "OLD" : "CURRENT", ++ is_old_mk ? "CURRENT" : "NEW"); ++ util_print_indented(msg, 0); ++ free(msg); ++ ++ rc = key_token_change(g.dll_CSNBKTC, (u8 *)key, keysize, ++ is_old_mk ? METHOD_OLD_TO_CURRENT : ++ METHOD_CURRENT_TO_NEW, ++ g.verbose); ++ if (rc != 0) { ++ warnx("Failed to re-encipher the secure volume key of device " ++ "'%s'", g.pos_arg); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ rc = crypt_keyslot_add_by_key(g.cd, CRYPT_ANY_SLOT, key, keysize, ++ password, password_len, ++ CRYPT_VOLUME_KEY_NO_SEGMENT); ++ if (rc < 0) { ++ warnx("Failed to add an unbound key slot to device '%s': %s", ++ g.pos_arg, strerror(-rc)); ++ goto out; ++ } ++ ++ reenc_tok.unbound_keyslot = rc; ++ pr_verbose("Re-enciphered volume key added to unbound key slot %d", ++ reenc_tok.unbound_keyslot); ++ ++ rc = ensure_is_unbound_keylot(reenc_tok.unbound_keyslot); ++ if (rc != 0) ++ goto out; ++ ++ if ((!is_old_mk && g.inplace) || ++ (is_old_mk && !g.staged)) { ++ if (!g.inplace) ++ printf("An in-place re-enciphering is performed.\n"); ++ ++ util_asprintf(&msg, "Re-enciphering has completed " ++ "successfully for device '%s'", g.pos_arg); ++ rc = activate_unbound_keyslot(-1, reenc_tok.unbound_keyslot, ++ key, keysize, password, ++ password_len, msg); ++ free(msg); ++ goto out; ++ } ++ ++ rc = put_reencipher_token(g.cd, CRYPT_ANY_TOKEN, &reenc_tok); ++ if (rc < 0) ++ goto out; ++ rc = 0; ++ ++ util_asprintf(&msg, "Staged re-enciphering is initiated for " ++ "device '%s'. After the NEW CCA master key has been set " ++ "to become the CURRENT master key, run 'zkey-cryptsetup " ++ "reencipher' with option '--complete' to complete the " ++ "re-enciphering process.", g.pos_arg, ++ program_invocation_short_name); ++ util_print_indented(msg, 0); ++ free(msg); ++ ++out: ++ secure_free(password, password_len); ++ secure_free(key, keysize); ++ ++ return rc; ++} ++ ++/* ++ * Completes a staged re-enciphering. ++ */ ++static int reencipher_complete(int token) ++{ ++ char vp[VERIFICATION_PATTERN_LEN]; ++ struct reencipher_token tok; ++ char *password = NULL; ++ size_t password_len; ++ char *key = NULL; ++ size_t keysize; ++ int is_old_mk; ++ char *prompt; ++ char *msg; ++ int rc; ++ ++ rc = get_reencipher_token(g.cd, token, &tok, true); ++ if (rc != 0) { ++ warnx("Failed to get the re-encipher token from device '%s': " ++ "%s", g.pos_arg, strerror(-rc)); ++ return rc; ++ } ++ ++ util_asprintf(&msg, "The re-enciphered secure volume key for " ++ "device '%s' is not valid.\nThe new CCA master key might " ++ "yet have to be set as the CURRENT master key.", ++ g.pos_arg); ++ util_asprintf(&prompt, "Enter passphrase for key slot %d of '%s': ", ++ tok.original_keyslot, g.pos_arg); ++ rc = validate_keyslot(tok.unbound_keyslot, &key, &keysize, &password, ++ &password_len, &is_old_mk, NULL, prompt, msg); ++ free(msg); ++ free(prompt); ++ if (rc < 0) ++ goto out; ++ ++ rc = ensure_is_unbound_keylot(rc); ++ if (rc != 0) ++ goto out; ++ ++ if (is_old_mk) { ++ util_asprintf(&msg, "The re-enciphered secure volume key " ++ "of device '%s' is enciphered with the CCA " ++ "master key from the OLD master key register. " ++ "The CCA master key might have changed again, " ++ "before the previous volume key re-enciphering " ++ "was completed.\n" ++ "Do you want to re-encipher the secure key with " ++ "the CCA master key in the CURRENT master key " ++ "register?", g.pos_arg); ++ util_print_indented(msg, 0); ++ free(msg); ++ ++ if (!prompt_for_yes()) { ++ warnx("Re-enciphering was aborted"); ++ rc = -ECANCELED; ++ goto out; ++ } ++ ++ rc = key_token_change(g.dll_CSNBKTC, (u8 *)key, keysize, ++ METHOD_OLD_TO_CURRENT, g.verbose); ++ if (rc != 0) { ++ warnx("Failed to re-encipher the secure volume key for " ++ "device '%s'", g.pos_arg); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ rc = crypt_keyslot_destroy(g.cd, tok.unbound_keyslot); ++ if (rc < 0) { ++ warnx("Failed to remove unbound key slot %d: %s", ++ tok.unbound_keyslot, strerror(-rc)); ++ } ++ ++ rc = crypt_keyslot_add_by_key(g.cd, CRYPT_ANY_SLOT, key, ++ keysize, password, password_len, ++ CRYPT_VOLUME_KEY_NO_SEGMENT); ++ if (rc < 0) { ++ warnx("Failed to add an unbound key slot to device " ++ "'%s': %s", g.pos_arg, strerror(-rc)); ++ goto out; ++ } ++ ++ tok.unbound_keyslot = rc; ++ pr_verbose("Re-enciphered volume key added to unbound key " ++ "slot %d", tok.unbound_keyslot); ++ ++ } ++ ++ rc = generate_key_verification_pattern(key, keysize, 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"); ++ goto out; ++ } ++ ++ if (strcmp(tok.verification_pattern, vp) != 0) { ++ warnx("The verification patterns of the new and old volume " ++ "keys do not match"); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ util_asprintf(&msg, "Re-enciphering has completed successfully for " ++ "device '%s'.", g.pos_arg); ++ rc = activate_unbound_keyslot(token, tok.unbound_keyslot, key, keysize, ++ password, password_len, msg); ++ free(msg); ++ ++out: ++ secure_free(password, password_len); ++ secure_free(key, keysize); ++ ++ return rc; ++} ++ ++ ++/* ++ * Command handler for 'reencipher'. ++ * ++ * Re-encipher a volume key of a volume encrypted with LUKS2 and the ++ * 'paes' cipher ++ */ ++static int command_reencipher(void) ++{ ++ int token; ++ int rc; ++ ++ if (g.inplace && g.staged) { ++ warnx("Options '--in-place|-i' and '--staged|-s' are " ++ "mutual exclusive"); ++ 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; ++ } ++ } ++ ++ token = find_token(g.cd, PAES_REENC_TOKEN_NAME); ++ ++ if (token < 0 && g.complete) { ++ warnx("Staged volume key re-enciphering is not pending for " ++ "device '%s'", g.pos_arg); ++ return EXIT_FAILURE; ++ } ++ ++ if (token < 0 || g.staged || g.inplace) ++ rc = reencipher_prepare(token); ++ else ++ rc = reencipher_complete(token); ++ ++ return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} ++ ++static void print_verification_pattern(const char *vp) ++{ ++ printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2, ++ vp); ++ printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2, ++ &vp[VERIFICATION_PATTERN_LEN / 2]); ++} ++ ++/* ++ * Command handler for 'validate'. ++ * ++ * Validate a volume key of a volume encrypted with LUKS2 and the ++ * 'paes' cipher ++ */ ++static int command_validate(void) ++{ ++ int reenc_pending = 0, vp_tok_avail = 0, is_valid = 0, is_old_mk = 0; ++ struct reencipher_token reenc_tok; ++ struct vp_token vp_tok; ++ size_t clear_keysize; ++ size_t keysize = 0; ++ char *key = NULL; ++ char *prompt; ++ char *msg; ++ int token; ++ int rc; ++ ++ util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg); ++ rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize, NULL, NULL, prompt); ++ free(prompt); ++ if (rc < 0) ++ goto out; ++ ++ rc = ensure_is_active_keylot(rc); ++ if (rc != 0) ++ goto out; ++ ++ rc = validate_secure_key(g.pkey_fd, (u8 *)key, keysize, &clear_keysize, ++ &is_old_mk, g.verbose); ++ is_valid = (rc == 0); ++ ++ token = find_token(g.cd, PAES_REENC_TOKEN_NAME); ++ if (token >= 0) { ++ rc = get_reencipher_token(g.cd, token, &reenc_tok, true); ++ if (rc == 0) ++ reenc_pending = 1; ++ } ++ ++ token = find_token(g.cd, PAES_VP_TOKEN_NAME); ++ if (token >= 0) { ++ rc = get_vp_token(g.cd, token, &vp_tok); ++ if (rc == 0) ++ vp_tok_avail = 1; ++ } ++ ++ printf("Validation of secure volume key of device '%s':\n", g.pos_arg); ++ printf(" Status: %s\n", is_valid ? "Valid" : "Invalid"); ++ printf(" Secure key size: %lu bytes\n", keysize); ++ printf(" XTS type key: %s\n", ++ keysize > SECURE_KEY_SIZE ? "Yes" : "No"); ++ if (is_valid) { ++ printf(" Clear key size: %lu bits\n", clear_keysize); ++ printf(" Enciphered with: %s CCA master key\n", ++ is_old_mk ? "OLD" : "CURRENT"); ++ } else { ++ printf(" Clear key size: (unknown)\n"); ++ printf(" Enciphered with: (unknown)\n"); ++ } ++ if (vp_tok_avail) ++ print_verification_pattern(vp_tok.verification_pattern); ++ else if (reenc_pending) ++ print_verification_pattern(reenc_tok.verification_pattern); ++ else ++ printf(" Verification pattern: Not available\n"); ++ ++ ++ if (reenc_pending) ++ printf(" Volume key re-enciphering is pending\n"); ++ ++ if (!is_valid) ++ printf("\nATTENTION: The secure volume key is not valid.\n"); ++ ++ if (is_old_mk) ++ util_print_indented("\nWARNING: The secure volume key is " ++ "currently enciphered with the OLD CCA " ++ "master key. To mitigate the danger of " ++ "data loss re-encipher the volume key with " ++ "the CURRENT CCA master key.", 0); ++ ++ if (is_valid && !vp_tok_avail) { ++ util_asprintf(&msg, "\nWARNING: The volume key cannot be " ++ "identified because the key verification pattern " ++ "token is not available in the LUKS2 header. Use " ++ "the '%s setvp' command to set the token.", ++ program_invocation_short_name); ++ util_print_indented(msg, 0); ++ free(msg); ++ } ++ ++ rc = is_valid ? 0 : -EINVAL; ++ ++out: ++ secure_free(key, keysize); ++ ++ return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} ++ ++/* ++ * Command handler for 'setvp'. ++ * ++ * Set the verification pattern token to allow identification of the key ++ */ ++static int command_setvp(void) ++{ ++ struct vp_token vp_tok; ++ size_t keysize = 0; ++ char *key = NULL; ++ char *prompt; ++ int token; ++ int rc; ++ ++ util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg); ++ rc = validate_keyslot(CRYPT_ANY_SLOT, &key, &keysize, NULL, NULL, ++ NULL, NULL, prompt, NULL); ++ free(prompt); ++ if (rc < 0) ++ goto out; ++ ++ rc = ensure_is_active_keylot(rc); ++ if (rc != 0) ++ goto out; ++ ++ token = find_token(g.cd, PAES_VP_TOKEN_NAME); ++ ++ rc = generate_key_verification_pattern(key, keysize, ++ vp_tok.verification_pattern, ++ sizeof(vp_tok.verification_pattern), ++ 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"); ++ goto out; ++ } ++ ++ rc = put_vp_token(g.cd, token, &vp_tok); ++ if (rc < 0) ++ goto out; ++ ++ rc = 0; ++ ++out: ++ secure_free(key, keysize); ++ ++ return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} ++ ++/* ++ * Command handler for 'setkey'. ++ * ++ * Set a new volume key to allow to recover from an invalid volume key ++ */ ++static int command_setkey(void) ++{ ++ char vp[VERIFICATION_PATTERN_LEN]; ++ size_t password_len = 0; ++ struct vp_token vp_tok; ++ size_t newkey_size = 0; ++ char *password = NULL; ++ size_t keysize = 0; ++ u8 *newkey = NULL; ++ char *key = NULL; ++ int is_old_mk; ++ char *prompt; ++ int keyslot; ++ char *msg; ++ int token; ++ int rc; ++ ++ if (g.master_key_file == NULL) { ++ misc_print_required_parm("--master-key-file/-m"); ++ return EXIT_FAILURE; ++ } ++ ++ newkey = read_secure_key(g.master_key_file, &newkey_size, g.verbose); ++ if (newkey == NULL) ++ return EXIT_FAILURE; ++ ++ rc = check_keysize_and_cipher_mode(newkey_size); ++ if (rc != 0) ++ goto out; ++ ++ rc = validate_secure_key(g.pkey_fd, newkey, newkey_size, NULL, ++ &is_old_mk, g.verbose); ++ if (rc != 0) { ++ warnx("The secure key in file '%s' is not valid", ++ g.master_key_file); ++ goto out; ++ } ++ ++ if (is_old_mk) { ++ util_asprintf(&msg, "The secure key in file '%s' is " ++ "enciphered with the CCA master key in the OLD " ++ "master key register. Do you want to set this " ++ "key as the new volume key anyway?", ++ g.master_key_file); ++ util_print_indented(msg, 0); ++ free(msg); ++ ++ if (!prompt_for_yes()) { ++ rc = -EINVAL; ++ goto out; ++ } ++ } ++ ++ util_asprintf(&prompt, "Enter passphrase for '%s': ", g.pos_arg); ++ rc = open_keyslot(CRYPT_ANY_SLOT, &key, &keysize, &password, ++ &password_len, prompt); ++ free(prompt); ++ if (rc < 0) ++ goto out; ++ ++ if (keysize != newkey_size) { ++ warnx("The secure key in file '%s' has an invalid size", ++ g.master_key_file); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ if (memcmp(newkey, key, keysize) == 0) { ++ warnx("The secure key in file '%s' is equal to the current " ++ "volume key, setkey is ignored", g.master_key_file); ++ rc = 0; ++ goto out; ++ } ++ ++ rc = generate_key_verification_pattern((char *)newkey, newkey_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"); ++ goto out; ++ } ++ ++ token = find_token(g.cd, PAES_VP_TOKEN_NAME); ++ if (token >= 0) { ++ rc = get_vp_token(g.cd, token, &vp_tok); ++ if (rc < 0) { ++ warnx("Failed to get the verification pattern token: " ++ "%s", strerror(-rc)); ++ goto out; ++ } ++ ++ if (strcmp(vp_tok.verification_pattern, vp) != 0) { ++ warnx("The verification patterns of the new and old " ++ "volume keys do not match"); ++ rc = -EINVAL; ++ goto out; ++ } ++ } else { ++ util_asprintf(&msg, "ATTENTION: The key validation pattern " ++ "token is not available in the LUKS2 header. " ++ "Thus, the new volume key cannot be confirmed to " ++ "be correct. You will lose all data on the " ++ "volume if you set the wrong volume key!\n" ++ "Are you sure that the key in file '%s' is the " ++ "correct volume key for volume '%s'?", ++ g.master_key_file, g.pos_arg); ++ util_print_indented(msg, 0); ++ free(msg); ++ ++ if (!prompt_for_yes()) { ++ rc = -EINVAL; ++ goto out; ++ } ++ } ++ ++ rc = crypt_keyslot_add_by_key(g.cd, CRYPT_ANY_SLOT, (char *)newkey, ++ newkey_size, password, password_len, ++ CRYPT_VOLUME_KEY_NO_SEGMENT); ++ if (rc < 0) { ++ warnx("Failed to add an unbound key slot to device '%s': %s", ++ g.pos_arg, strerror(-rc)); ++ goto out; ++ } ++ keyslot = rc; ++ ++ rc = ensure_is_unbound_keylot(keyslot); ++ if (rc != 0) ++ goto out; ++ ++ pr_verbose("New volume key added to unbound key slot %d", keyslot); ++ ++ util_asprintf(&msg, "The volume key has been successfully set for " ++ "device '%s'", g.pos_arg); ++ rc = activate_unbound_keyslot(-1, keyslot, (char *)newkey, newkey_size, ++ password, password_len, msg); ++ free(msg); ++ if (rc < 0) ++ goto out; ++ ++ memcpy(vp_tok.verification_pattern, vp, ++ sizeof(vp_tok.verification_pattern)); ++ rc = put_vp_token(g.cd, token, &vp_tok); ++ if (rc < 0) ++ goto out; ++ ++ rc = 0; ++ ++out: ++ secure_free(password, password_len); ++ secure_free(newkey, newkey_size); ++ secure_free(key, keysize); ++ ++ return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS; ++} ++ ++ ++static bool is_command(struct zkey_cryptsetup_command *command, const char *str) ++{ ++ char command_str[ZKEY_CRYPTSETUP_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_cryptsetup_command *find_command(const char *command) ++{ ++ struct zkey_cryptsetup_command *cmd = zkey_cryptsetup_commands; ++ ++ while (cmd->command) { ++ if (is_command(cmd, command)) ++ return cmd; ++ cmd++; ++ } ++ return NULL; ++} ++ ++/* ++ * Entry point ++ */ ++int main(int argc, char *argv[]) ++{ ++ struct zkey_cryptsetup_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 'c': ++ g.complete = 1; ++ break; ++ case 'i': ++ g.inplace = 1; ++ break; ++ case 's': ++ g.staged = 1; ++ break; ++ case 'd': ++ g.keyfile = optarg; ++ break; ++ case 'o': ++ 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'|" ++ "'-o': '%s'", optarg); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ break; ++ case 'l': ++ 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'|" ++ "'-l': '%s'", optarg); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ break; ++ case 'T': ++ 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'|'-T': '%s'", ++ optarg); ++ util_prg_print_parse_error(); ++ return EXIT_FAILURE; ++ } ++ break; ++ case 'm': ++ g.master_key_file = optarg; ++ break; ++ case 'D': ++ g.debug = true; ++ g.verbose = true; ++ break; ++ case 'V': ++ g.verbose = true; ++ break; ++ 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->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; ++ } ++ } ++ ++ crypt_set_log_callback(NULL, cryptsetup_log, NULL); ++ if (g.debug) ++ crypt_set_debug_level(-1); ++ ++ if (command->open_device) { ++ if (g.pos_arg == NULL) { ++ misc_print_required_parm(command->pos_arg); ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ ++ rc = open_device(g.pos_arg, &g.cd); ++ if (rc != 0) { ++ g.cd = NULL; ++ rc = EXIT_FAILURE; ++ goto out; ++ } ++ } ++ ++ set_int_handler(); ++ ++ rc = command->function(); ++ ++out: ++ if (g.lib_csulcca) ++ dlclose(g.lib_csulcca); ++ if (g.pkey_fd >= 0) ++ close(g.pkey_fd); ++ if (g.cd) ++ crypt_free(g.cd); ++ return rc; ++} diff --git a/s390-tools-sles15sp1-0016-zkey-Add-man-page-for-zkey-cryptsetup.patch b/s390-tools-sles15sp1-0016-zkey-Add-man-page-for-zkey-cryptsetup.patch new file mode 100644 index 0000000..3a7f40f --- /dev/null +++ b/s390-tools-sles15sp1-0016-zkey-Add-man-page-for-zkey-cryptsetup.patch @@ -0,0 +1,443 @@ +Subject: zkey: Add man page for zkey-cryptsetup +From: Ingo Franzki + +Summary: zkey: Support CCA master key change with LUKS2 volumes using paes +Description: Support the usage of protected key crypto for dm-crypt disks in + LUKS2 format by providing a tool allowing to re-encipher a + secure LUKS2 volume key when the CCA master key is changed +Upstream-ID: 5e65df7375aec81d9348a57cdcbccb89a65422c3 +Problem-ID: SEC1424.1 + +Upstream-Description: + + zkey: Add man page for zkey-cryptsetup + + Add documentation for the new zkey-cryptsetup tool + + Signed-off-by: Ingo Franzki + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Ingo Franzki +--- + zkey/Makefile | 1 + zkey/zkey-cryptsetup.1 | 403 +++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 404 insertions(+) + +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -42,6 +42,7 @@ install: all + $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zkey-cryptsetup $(DESTDIR)$(USRBINDIR) + $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1 + $(INSTALL) -m 644 -c zkey.1 $(DESTDIR)$(MANDIR)/man1 ++ $(INSTALL) -m 644 -c zkey-cryptsetup.1 $(DESTDIR)$(MANDIR)/man1 + $(INSTALL) -d -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey + $(INSTALL) -d -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey/repository + +--- /dev/null ++++ b/zkey/zkey-cryptsetup.1 +@@ -0,0 +1,403 @@ ++.\" Copyright IBM Corp. 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. ++.\" ++.TH ZKEY\-CRYPTSETUP 1 "May 2018" "s390-tools" ++.SH NAME ++zkey\-cryptsetup \- Manage secure AES volume keys of volumes encrypted with ++\fBLUKS2\fP and the \fBpaes\fP cipher ++. ++. ++.SH SYNOPSIS ++.B zkey\-cryptsetup ++.I command ++.I device ++.RI [ OPTIONS ] ++. ++.PP ++.B zkey\-cryptsetup ++.RI [ command ] ++.BR \-\-help | \-h ++.br ++.B zkey\-cryptsetup ++.BR \-\-version | \-v ++. ++. ++. ++.SH DESCRIPTION ++Use \fBzkey\-cryptsetup\fP to validate and re-encipher secure AES ++volume keys of volumes encrypted with \fBLUKS2\fP and the \fBpaes\fP cipher. ++These secure AES volume keys are enciphered with a master key of an IBM ++cryptographic adapter in CCA coprocessor mode. ++.PP ++To encrypt a volume using \fBLUKS2\fP and the \fBpaes\fP cipher, generate a ++secure AES key using \fBzkey\fP: \fB'zkey generate luks.key --xts'\fP. ++Then format the device with \fBcryptsetup\fP using the just generated secure ++AES key from file luks.key: \fB'cryptsetup luksFormat --type luks2 ++--cipher paes-xts-plain64 --master-key-file luks.key --key-size 1024'\fP. For ++more details about \fBzkey\fP or \fBcryptsetup\fP see the ++corresponding man pages. ++. ++. ++. ++.SH COMMANDS ++. ++. ++.SS "Validate secure AES volume keys" ++. ++.B zkey\-cryptsetup ++.BR validate | val ++.I device ++.RB [ \-\-key\-file | \-d ++.IR file-name ] ++.RB [ \-\-keyfile\-offset | \-o ++.IR bytes ] ++.RB [ \-\-keyfile\-size | \-l ++.IR bytes ] ++.RB [ \-\-tries | \-T ++.IR number ] ++.RB [ \-\-verbose | \-V ] ++.RB [ \-\-debug | \-D ] ++.PP ++Use the ++.B validate ++command to validate a secure AES volume key of a volume encrypted with ++\fBLUKS2\fP and the \fBpaes\fP cipher. ++It checks if the LUKS2 header of the volume contains a valid secure key. ++It also displays the attributes of the secure key, such as key size, whether ++it is a secure key that can be used for the XTS cipher mode, and the master key ++register (CURRENT or OLD) with which the secure key is enciphered. ++For further information about master key registers, see the ++\fBreencipher\fP command. ++.PP ++To open a key slot contained in the LUKS2 header of the volume, a passphrase is ++required. You are prompted for the passphrase, unless option ++.B \-\-key\-file ++is specified. Option ++.B \-\-tries ++specifies how often a passphrase can be re-entered. When option ++.B \-\-key\-file ++is specified, the passphrase is read from the specified file. You can specify ++options ++.B \-\-keyfile\-offset ++and ++.B \-\-keyfile\-size ++to control which part of the key file is used as passphrase. These options ++behave in the same way as with \fBcryptsetup\fP. ++. ++.SS "Re-encipher secure AES volume keys" ++. ++.PP ++.B zkey\-cryptsetup ++.BR reencipher | re ++.I device ++.RB [ \-\-staged | \-s ] ++.RB [ \-\-in\-place | \-i ] ++.RB [ \-\-complete | \-c ] ++.RB [ \-\-key\-file | \-d ++.IR file-name ] ++.RB [ \-\-keyfile\-offset | \-o ++.IR bytes ] ++.RB [ \-\-keyfile\-size | \-l ++.IR bytes ] ++.RB [ \-\-tries | \-T ++.IR number ] ++.RB [ \-\-verbose | \-V ] ++.RB [ \-\-debug | \-D ] ++.PP ++Use the ++.B reencipher ++command to re-encipher a secure AES volume key of a volume encrypted with ++\fBLUKS2\fP and the \fBpaes\fP cipher. A secure AES volume key must be ++re-enciphered when the master key of the cryptographic adapter in CCA ++coprocessor mode changes. ++.PP ++The cryptographic adapter in CCA coprocessor mode has three different registers ++to store master keys: ++.RS 2 ++.IP "\(bu" 2 ++The \fBCURRENT\fP register contains the current master key. ++. ++.IP "\(bu" 2 ++The \fBOLD\fP register contains the previously used master key. ++Secure keys enciphered with the master key contained in the \fBOLD\fP ++register can still be used until the master key is changed again. ++. ++.IP "\(bu" 2 ++The \fBNEW\fP register contains the new master key to be set. ++The master key in the \fBNEW\fP register cannot be used until it is made ++the current master key. You can pro-actively re-encipher a secure key with the ++\fBNEW\fP master key before this key is made the \fBCURRENT\fP key. ++.RE ++.PP ++\fBzkey\-cryptsetup\fP automatically detects whether the secure volume key ++is currently enciphered with the master key in the \fBOLD\fP register or with ++the master key in the \fBCURRENT\fP register. If currently enciphered with the ++master key in the \fBOLD\fP register, it is re-enciphered with the master key ++in the \fBCURRENT\fP register. If it is currently enciphered with the master ++key in the \fBCURRENT\fP register, it is re-enciphered with the master key in ++the \fBNEW\fP register. If for this case the \fBNEW\fP register does not ++contain a valid master key, then the re-encipher operation fails. ++.PP ++Re-enciphering a secure volume key of a volume encrypted with ++\fBLUKS2\fP and the \fBpaes\fP cipher can be performed \fBin-place\fP, or in ++\fBstaged\fP mode. ++.PP ++\fB"In-place"\fP immediately replaces the secure volume key in the LUKS2 ++header of the encrypted volume with the re-enciphered secure volume key. ++Re-enciphering from \fBOLD\fP to \fBCURRENT\fP is performed in-place per ++default. You can use option \fB--in-place\fP to force an in-place ++re-enciphering for the \fBCURRENT\fP to \fBNEW\fP case. Be aware that ++an encrypted volume with a secure volume key that was re-enciphered in-place ++from \fBCURRENT\fP to \fBNEW\fP is no longer usable, until the new CCA master ++key has been made the current one. ++.PP ++\fBStaged\fP mode means that the re-enciphered secure volume key is stored in a ++separate (unbound) key slot in the LUKS2 header of the encrypted volume. Thus ++all key slots containing the current secure volume key are still valid at this ++point. Once the new CCA master key has been set (made active), you must rerun ++the reencipher command with option \fB--complete\fP to complete the staged ++re-enciphering. When completing the staged re-enciphering, the (unbound) key ++slot containing the re-enciphered secure volume key becomes the active ++key slot and, optionally, all key slots containing the old secure volume key ++are removed. ++Re-enciphering from \fBCURRENT\fP to \fBNEW\fP is performed in staged mode per ++default. You can use option \fB--staged\fP to force a staged re-enciphering for ++the \fBOLD\fP to \fBCURRENT\fP case. ++.PP ++To open a key slot contained in the LUKS2 header of the volume, a passphrase is ++required. You are prompted for the passphrase, unless option ++.B \-\-key\-file ++is specified. Option ++.B \-\-tries ++specifies how often a passphrase can be re-entered. When option ++.B \-\-key\-file ++is specified, the passphrase is read from the specified file. You can specify ++options ++.B \-\-keyfile\-offset ++and ++.B \-\-keyfile\-size ++to control which part of the key file is used as passphrase. These options ++behave in the same way as with \fBcryptsetup\fP. ++.PP ++.B Note: ++The \fBreencipher\fP command requires the CCA host library (libcsulcca.so) ++to be installed. ++. ++. ++. ++.SS "Set a verification pattern of the secure AES volume key" ++. ++.B zkey\-cryptsetup ++.BR setvp | setv ++.I device ++.RB [ \-\-key\-file | \-d ++.IR file-name ] ++.RB [ \-\-keyfile\-offset | \-o ++.IR bytes ] ++.RB [ \-\-keyfile\-size | \-l ++.IR bytes ] ++.RB [ \-\-tries | \-T ++.IR number ] ++.RB [ \-\-verbose | \-V ] ++.RB [ \-\-debug | \-D ] ++.PP ++Use the ++.B setvp ++command to set a verification pattern of the secure AES volume key of a volume ++encrypted with \fBLUKS2\fP and the \fBpaes\fP cipher. The verification pattern ++identifies the effective key used to encrypt the volume's data. ++The verification pattern is stored in a token named ++\fBpaes-verification-pattern\fP in the LUKS2 header. ++.PP ++.B Note: ++Set the verification pattern right after formatting the volume using ++\fB'cryptsetup luksFormat'\fP. ++.PP ++To open a key slot contained in the LUKS2 header of the volume, a passphrase is ++required. You are prompted for the passphrase, unless option ++.B \-\-key\-file ++is specified. Option ++.B \-\-tries ++specifies how often a passphrase can be re-entered. When option ++.B \-\-key\-file ++is specified, the passphrase is read from the specified file. You can specify ++options ++.B \-\-keyfile\-offset ++and ++.B \-\-keyfile\-size ++to control which part of the key file is used as passphrase. These options ++behave in the same way as with \fBcryptsetup\fP. ++. ++. ++. ++.SS "Set a new secure AES volume key for a volume" ++. ++.B zkey\-cryptsetup ++.BR setkey | setk ++.I device ++.BR \-\-master\-key\-file | \-m ++.IR file-name ++.RB [ \-\-key\-file | \-d ++.IR file-name ] ++.RB [ \-\-keyfile\-offset | \-o ++.IR bytes ] ++.RB [ \-\-keyfile\-size | \-l ++.IR bytes ] ++.RB [ \-\-tries | \-T ++.IR number ] ++.RB [ \-\-verbose | \-V ] ++.RB [ \-\-debug | \-D ] ++.PP ++Use the ++.B setkey ++command to set a new secure AES volume key for a volume encrypted with ++\fBLUKS2\fP and the \fBpaes\fP cipher. Use this command to recover from an ++invalid secure AES volume key contained in the LUKS2 header. ++A secure AES volume key contained in the LUKS2 header can become invalid when ++the CCA master key is changed without re-enciphering the secure volume key. ++.PP ++You can recover the secure volume key only if you have a copy of the secure key ++in a file, and this copy was re-enciphered when the CCA master key has been ++changed. Thus, the copy of the secure key must be currently enciphered with the ++CCA master key in the CURRENT or OLD master key register. ++Specify the secure key file with option ++.B \-\-master\-key\-file ++to set this secure key as the new volume key. ++.PP ++In case the LUKS2 header of the volume contains a verification pattern token, ++it is used to ensure that the new volume key contains the same effective key. ++If no verification pattern token is available, then you are prompted to confirm ++that the specified secure key is the correct one. ++.B ATTENTION: ++If you set a wrong secure key you will loose all the data on the encrypted ++volume! ++.PP ++To open a key slot contained in the LUKS2 header of the volume, a passphrase is ++required. You are prompted for the passphrase, unless option ++.B \-\-key\-file ++is specified. Option ++.B \-\-tries ++specifies how often a passphrase can be re-entered. When option ++.B \-\-key\-file ++is specified, the passphrase is read from the specified file. You can specify ++options ++.B \-\-keyfile\-offset ++and ++.B \-\-keyfile\-size ++to control which part of the key file is used as passphrase. These options ++behave in the same way the same as with \fBcryptsetup\fP. ++. ++. ++. ++. ++.SH OPTIONS ++. ++.SS "Options for the reencipher command" ++.TP ++.BR \-i ", " \-\-in-place ++Forces an in-place re-enciphering of a secure volume key in the LUKS2 ++header. This option immediately replaces the secure volume key in the LUKS2 ++header of the encrypted volume with the re-enciphered secure volume key. ++Re-enciphering from \fBOLD\fP to \fBCURRENT\fP is performed in-place per ++default. ++.TP ++.BR \-s ", " \-\-staged ++Forces that the re-enciphering of a secure volume key in the LUKS2 ++header is performed in staged mode. Staged mode means that the re-enciphered ++secure volume key is stored in a separate (unbound) key slot in the LUKS2 ++header of the encrypted volume. Thus all key slots containing the current ++secure volume key are still valid at this point. Once the new CCA master key ++has been set (made active), you must rerun the reencipher command with option ++\fB--complete\fP to complete the staged re-enciphering. Re-enciphering from ++\fBCURRENT\fP to \fBNEW\fP is performed in staged mode per default. ++.TP ++.BR \-p ", " \-\-complete ++Completes a staged re-enciphering. Use this option after the new CCA master key ++has been set (made active). When completing the staged re-enciphering, the ++(unbound) key slot containing the re-enciphered secure volume key becomes ++the active key slot and, optionally, all key slots containing the old secure ++volume key are removed. ++. ++. ++. ++.SS "Options for the setkey command" ++.TP ++.BR \-m ", " \-\-master\-key\-file\~\fIfile\-name\fP ++Specifies the name of a file containing the secure AES key that is set as the ++new volume key. ++. ++. ++. ++.SS "Options for supplying the passphrase" ++.TP ++.BR \-d ", " \-\-key\-file\~\fIfile\-name\fP ++Reads the passphrase from the specified file. If this option is omitted, ++or if the file\-name is \fI-\fP (a dash), then you are prompted to enter the ++passphrase interactively. ++.TP ++.BR \-o ", " \-\-keyfile\-offset\~\fIbytes\fP ++Specifies the number of bytes to skip before starting to read in the file ++specified with option \fB\-\-key\-file\fP. If omitted, the file is read ++from the beginning. When option \fB\-\-key\-file\fP is not specified, this ++option is ignored. ++.TP ++.BR \-l ", " \-\-keyfile\-size\~\fIbytes\fP ++Specifies the number of bytes to be read from the beginning of the file ++specified with option \fB\-\-key\-file\fP. If omitted, the file is read ++until the end. When \fB\-\-keyfile\-offset\fP is also specified, reading starts ++at the offset. When option \fB\-\-key\-file\fP is not specified, this option is ++ignored. ++.TP ++.BR \-T ", " \-\-tries\~\fInumber\fP ++Specifies how often the interactive input of the passphrase can be re-entered. ++The default is 3 times. When option \fB\-\-key\-file\fP is specified, this ++option is ignored, and the passphrase is read only once from the file. ++. ++. ++. ++.SS "General options" ++.TP ++.BR \-V ", " \-\-verbose ++Displays additional information messages during processing. ++.TP ++.BR \-D ", " \-\-debug ++Displays additional debugging messages during processing. This option also ++implies \fB\-\-verbose\fP. ++.TP ++.BR \-h ", " \-\-help ++Displays help text and exits. ++.TP ++.BR \-v ", " \-\-version ++Displays version information and exits. ++. ++. ++. ++.SH EXAMPLES ++.TP ++.B zkey-cryptsetup reencipher /dev/dasdd1 ++Re-enciphers the secure volume key of the encrypted volume /dev/dasdd1. ++.TP ++.B zkey-cryptsetup reencipher /dev/dasdd1 \-\-staged ++Re-enciphers the secure volume key of the encrypted volume /dev/dasdd1 in ++staged mode. ++.TP ++.B zkey-cryptsetup reencipher /dev/dasdd1 \-\-complete ++Completes re-enciphers the secure volume key of the encrypted ++volume /dev/dasdd1. ++.TP ++.B zkey-cryptsetup reencipher /dev/dasdd1 \-\-in\-place ++Re-enciphers the secure volume key of the encrypted volume /dev/dasdd1 in ++in-place mode. ++.TP ++.B zkey-cryptsetup validate /dev/dasdd1 ++Validates the secure volume key of the encrypted volume /dev/dasdd1 and ++displays its attributes. ++.TP ++.B zkey-cryptsetup setvp /dev/dasdd1 ++Sets the verification pattern of the secure volume key of the encrypted ++volume /dev/dasdd1. ++.TP ++.B zkey-cryptsetup setkey /dev/dasdd1 --master-key-file seckey.key ++Sets the secure key contained in file seckey.key as the new volume key ++for the encrypted volume /dev/dasdd1. diff --git a/s390-tools-sles15sp1-0017-zkey-Add-build-dependency-for-libcryptsetup-and-json.patch b/s390-tools-sles15sp1-0017-zkey-Add-build-dependency-for-libcryptsetup-and-json.patch new file mode 100644 index 0000000..9c5d252 --- /dev/null +++ b/s390-tools-sles15sp1-0017-zkey-Add-build-dependency-for-libcryptsetup-and-json.patch @@ -0,0 +1,188 @@ +Subject: zkey: Add build dependency for libcryptsetup and json-c +From: Ingo Franzki + +Summary: zkey: Support CCA master key change with LUKS2 volumes using paes +Description: Support the usage of protected key crypto for dm-crypt disks in + LUKS2 format by providing a tool allowing to re-encipher a + secure LUKS2 volume key when the CCA master key is changed +Upstream-ID: 818ffbc4b05783851cc12682d3d8ad6b99312d63 +Problem-ID: SEC1424.1 + +Upstream-Description: + + zkey: Add build dependency for libcryptsetup and json-c + + The zkey-cryptsetup tool has a build dependency to + libcryptsetup version 2.0.3 or later, and json-c. + + Signed-off-by: Ingo Franzki + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Ingo Franzki +--- + README.md | 9 ++++-- + common.mak | 3 +- + zkey/Makefile | 84 +++++++++++++++++++++++++++++++++++++++++++--------------- + 3 files changed, 72 insertions(+), 24 deletions(-) + +--- a/README.md ++++ b/README.md +@@ -264,6 +264,8 @@ build options: + | pfm | `HAVE_PFM` | cpacfstats | + | net-snmp | `HAVE_SNMP` | osasnmpd | + | openssl | `HAVE_OPENSSL` | zkey | ++| cryptsetup | `HAVE_CRYPTSETUP2` | zkey-cryptsetup | ++| json-c | `HAVE_JSONC` | zkey-cryptsetup | + + This table lists additional build or install options: + +@@ -369,8 +371,11 @@ the different tools are provided: + + * zkey: + For building the zkey tools you need openssl version 0.9.7 or newer installed +- (openssl-devel.rpm). Tip: you may skip the zkey build by adding +- `HAVE_OPENSSL=0` to the make invocation. ++ (openssl-devel.rpm). Also required are cryptsetup version 2.0.3 or newer ++ (cryptsetup-devel.rpm), and json-c version 0.12 or newer (json-c-devel.rpm). ++ Tip: you may skip the zkey build by adding `HAVE_OPENSSL=0`, and you may ++ may skip the zkey-cryptsetup build by adding `HAVE_CRYPTSETUP2=0`, or ++ `HAVE_JSONC=0` to the make invocation. + A new group 'zkeyadm' needs to be created and all users intending to use the + tool must be added to this group. The owner of the default key repository + '/etc/zkey/repository' must be set to group 'zkeyadm' with write permission +--- a/common.mak ++++ b/common.mak +@@ -113,9 +113,10 @@ DEFAULT_LDFLAGS = -rdynamic + # $2: Name of include file to check + # $3: Name of required devel package + # $4: Option to skip build (e.g. HAVE_FUSE=0) ++# $5: Additional compiler & linker options (optional) + # + check_dep=\ +-printf "\#include <%s>" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) -c -o /dev/null -xc - ) > /dev/null 2>&1; \ ++printf "\#include <%s>\n int main(void) {return 0;}" $2 | ( $(CC) $(filter-out --coverage, $(ALL_CFLAGS)) $(ALL_CPPFLAGS) $5 -o /dev/null -xc - ) > /dev/null 2>&1; \ + if [ $$? != 0 ]; \ + then \ + printf " REQCHK %s (%s)\n" $1 $2; \ +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -1,54 +1,96 @@ + include ../common.mak + +-ifeq (${HAVE_OPENSSL},0) ++ifneq (${HAVE_OPENSSL},0) ++ BUILD_TARGETS += zkey ++ INSTALL_TARGETS += install-zkey ++else ++ BUILD_TARGETS += zkey-skip ++ INSTALL_TARGETS += zkey-skip ++endif + +-all: +- $(SKIP) HAVE_OPENSSL=0 ++ifneq (${HAVE_CRYPTSETUP2},0) ++ ifneq (${HAVE_JSONC},0) ++ BUILD_TARGETS += zkey-cryptsetup ++ INSTALL_TARGETS += install-zkey-cryptsetup ++ else ++ BUILD_TARGETS += zkey-cryptsetup-skip-jsonc ++ INSTALL_TARGETS += zkey-cryptsetup-skip-jsonc ++ endif ++else ++ BUILD_TARGETS += zkey-cryptsetup-skip-cryptsetup2 ++ INSTALL_TARGETS += zkey-cryptsetup-skip-cryptsetup2 ++endif + +-install: +- $(SKIP) HAVE_OPENSSL=0 ++CPPFLAGS += -I../include ++LIBS = $(rootdir)/libutil/libutil.a + +-else ++detect-libcryptsetup.h: ++ echo "#include " > detect-libcryptsetup.h ++ echo "#ifndef CRYPT_LUKS2" >> detect-libcryptsetup.h ++ echo " #error libcryptsetup version 2.0.3 is required" >> detect-libcryptsetup.h ++ echo "#endif" >> detect-libcryptsetup.h ++ echo "int i = CRYPT_SLOT_UNBOUND;" >> detect-libcryptsetup.h + +-check_dep: ++check-dep-zkey: + $(call check_dep, \ + "zkey", \ + "openssl/evp.h", \ + "openssl-devel", \ + "HAVE_OPENSSL=0") + +-CPPFLAGS += -I../include ++check-dep-zkey-cryptsetup: detect-libcryptsetup.h ++ $(call check_dep, \ ++ "zkey-cryptsetup", \ ++ "detect-libcryptsetup.h", \ ++ "cryptsetup-devel version 2.0.3", \ ++ "HAVE_CRYPTSETUP2=0", \ ++ "-I.") ++ $(call check_dep, \ ++ "zkey-cryptsetup", \ ++ "json-c/json.h", \ ++ "json-c-devel", \ ++ "HAVE_JSONC=0") ++ ++zkey-skip: ++ echo " SKIP zkey due to HAVE_OPENSSL=0" ++ ++zkey-cryptsetup-skip-cryptsetup2: ++ echo " SKIP zkey-cryptsetup due to HAVE_CRYPTSETUP2=0" + +-all: check_dep zkey zkey-cryptsetup ++zkey-cryptsetup-skip-jsonc: ++ echo " SKIP zkey-cryptsetup due to HAVE_JSONC=0" + +-libs = $(rootdir)/libutil/libutil.a ++all: $(BUILD_TARGETS) + + zkey.o: zkey.c pkey.h misc.h + pkey.o: pkey.c pkey.h +-properties.o: properties.c properties.h ++properties.o: check-dep-zkey properties.c properties.h + keystore.o: keystore.c keystore.h properties.h +-zkey-cryptsetup.o: zkey-cryptsetup.c pkey.h misc.h ++zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h misc.h + + zkey: LDLIBS = -ldl -lcrypto +-zkey: zkey.o pkey.o properties.o keystore.o $(libs) ++zkey: zkey.o pkey.o properties.o keystore.o $(LIBS) + + zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c +-zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(libs) ++zkey-cryptsetup: zkey-cryptsetup.o pkey.o $(LIBS) + +- +-install: all ++install-common: + $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) +- $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zkey $(DESTDIR)$(USRBINDIR) +- $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zkey-cryptsetup $(DESTDIR)$(USRBINDIR) + $(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man1 ++ ++install-zkey: ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zkey $(DESTDIR)$(USRBINDIR) + $(INSTALL) -m 644 -c zkey.1 $(DESTDIR)$(MANDIR)/man1 +- $(INSTALL) -m 644 -c zkey-cryptsetup.1 $(DESTDIR)$(MANDIR)/man1 + $(INSTALL) -d -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey + $(INSTALL) -d -m 770 $(DESTDIR)$(SYSCONFDIR)/zkey/repository + +-endif ++install-zkey-cryptsetup: ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zkey-cryptsetup $(DESTDIR)$(USRBINDIR) ++ $(INSTALL) -m 644 -c zkey-cryptsetup.1 $(DESTDIR)$(MANDIR)/man1 ++ ++install: all install-common $(INSTALL_TARGETS) + + clean: +- rm -f *.o zkey zkey-cryptsetup ++ rm -f *.o zkey zkey-cryptsetup detect-libcryptsetup.h + + .PHONY: all install clean diff --git a/s390-tools-sles15sp1-0018-zkey-Add-key-verification-pattern-property.patch b/s390-tools-sles15sp1-0018-zkey-Add-key-verification-pattern-property.patch new file mode 100644 index 0000000..71e0760 --- /dev/null +++ b/s390-tools-sles15sp1-0018-zkey-Add-key-verification-pattern-property.patch @@ -0,0 +1,349 @@ +Subject: zkey: Add key verification pattern property +From: Ingo Franzki + +Summary: zkey: Support CCA master key change with LUKS2 volumes using paes +Description: Support the usage of protected key crypto for dm-crypt disks in + LUKS2 format by providing a tool allowing to re-encipher a + secure LUKS2 volume key when the CCA master key is changed +Upstream-ID: 512b47c0042a3cdedafce8d46dcc76053298116c +Problem-ID: SEC1424.1 + +Upstream-Description: + + zkey: Add key verification pattern property + + Store a verification pattern in the properties file along + with the secure key. The verification pattern allows to identify + the inner key even when the secure key is no longer valid. + + Signed-off-by: Ingo Franzki + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Ingo Franzki +--- + zkey/keystore.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++----- + zkey/zkey.1 | 4 - + zkey/zkey.c | 27 +++++++++-- + 3 files changed, 145 insertions(+), 18 deletions(-) + +--- a/zkey/keystore.c ++++ b/zkey/keystore.c +@@ -58,6 +58,7 @@ struct key_filenames { + #define PROP_NAME_CREATION_TIME "creation-time" + #define PROP_NAME_CHANGE_TIME "update-time" + #define PROP_NAME_REENC_TIME "reencipher-time" ++#define PROP_NAME_KEY_VP "verification-pattern" + + #define IS_XTS(secure_key_size) (secure_key_size > SECURE_KEY_SIZE ? 1 : 0) + +@@ -75,6 +76,7 @@ struct key_filenames { + #define REC_CREATION_TIME "Created" + #define REC_CHANGE_TIME "Changed" + #define REC_REENC_TIME "Re-enciphered" ++#define REC_KEY_VP "Verification pattern" + + #define pr_verbose(keystore, fmt...) do { \ + if (keystore->verbose) \ +@@ -1270,6 +1272,77 @@ struct keystore *keystore_new(const char + } + + /** ++ * Generate the key verification pattern from the specified secure key file ++ * ++ * @param[in] keystore the key store ++ * @param[in} keyfile the key file ++ * @param[in] vp buffer filled with the verification pattern ++ * @param[in] vp_len length of the buffer. Must be at ++ * least VERIFICATION_PATTERN_LEN bytes in size. ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++static int _keystore_generate_verification_pattern(struct keystore *keystore, ++ const char *keyfile, ++ char *vp, size_t vp_len) ++{ ++ size_t key_size; ++ u8 *key; ++ int rc; ++ ++ util_assert(keystore != NULL, "Internal error: keystore is NULL"); ++ util_assert(keyfile != NULL, "Internal error: keyfile is NULL"); ++ util_assert(vp != NULL, "Internal error: vp is NULL"); ++ ++ key = read_secure_key(keyfile, &key_size, keystore->verbose); ++ if (key == NULL) ++ return -EIO; ++ ++ rc = generate_key_verification_pattern((const char *)key, key_size, ++ vp, vp_len, keystore->verbose); ++ ++ free(key); ++ return rc; ++} ++ ++/** ++ * Checks if the key verification pattern property exists. If not, then it is ++ * created from the secure key. ++ * ++ * @param[in] keystore the key store ++ * @param[in] file_names the file names of the key ++ * @param[in] key_props the properties of the key ++ * ++ * @returns 0 for success or a negative errno in case of an error ++ */ ++static int _keystore_ensure_vp_exists(struct keystore *keystore, ++ const struct key_filenames *file_names, ++ struct properties *key_props) ++{ ++ char vp[VERIFICATION_PATTERN_LEN]; ++ char *temp; ++ int rc; ++ ++ temp = properties_get(key_props, PROP_NAME_KEY_VP); ++ if (temp != NULL) { ++ free(temp); ++ return 0; ++ } ++ ++ rc = _keystore_generate_verification_pattern(keystore, ++ file_names->skey_filename, ++ vp, sizeof(vp)); ++ if (rc != 0) ++ return rc; ++ ++ rc = properties_set(key_props, PROP_NAME_KEY_VP, vp); ++ if (rc != 0) ++ return rc; ++ ++ return 0; ++} ++ ++/** + * Sets a timestamp to be used as creation/update/reencipher time into + * the specified property + * +@@ -1348,7 +1421,7 @@ static int _keystore_set_default_propert + */ + static int _keystore_create_info_file(struct keystore *keystore, + const char *name, +- const char *info_filename, ++ const struct key_filenames *filenames, + const char *description, + const char *volumes, const char *apqns, + size_t sector_size) +@@ -1396,17 +1469,26 @@ static int _keystore_create_info_file(st + goto out; + } + +- rc = properties_save(key_props, info_filename, 1); ++ rc = _keystore_ensure_vp_exists(keystore, filenames, key_props); ++ if (rc != 0) { ++ warnx("Failed to generate the key verification pattern: %s", ++ strerror(-rc)); ++ warnx("Make sure that kernel module 'paes_s390' is loaded and " ++ "that the 'paes' cipher is available"); ++ return rc; ++ } ++ ++ rc = properties_save(key_props, filenames->info_filename, 1); + if (rc != 0) { + pr_verbose(keystore, + "Key info file '%s' could not be written: %s", +- info_filename, strerror(-rc)); ++ filenames->info_filename, strerror(-rc)); + goto out; + } + +- rc = _keystore_set_file_permission(keystore, info_filename); ++ rc = _keystore_set_file_permission(keystore, filenames->info_filename); + if (rc != 0) { +- remove(info_filename); ++ remove(filenames->info_filename); + goto out; + } + +@@ -1519,8 +1601,7 @@ int keystore_generate_key(struct keystor + if (rc != 0) + goto out_free_props; + +- rc = _keystore_create_info_file(keystore, name, +- file_names.info_filename, ++ rc = _keystore_create_info_file(keystore, name, &file_names, + description, volumes, apqns, + sector_size); + if (rc != 0) +@@ -1603,8 +1684,7 @@ int keystore_import_key(struct keystore + if (rc != 0) + goto out_free_props; + +- rc = _keystore_create_info_file(keystore, name, +- file_names.info_filename, ++ rc = _keystore_create_info_file(keystore, name, &file_names, + description, volumes, apqns, + sector_size); + if (rc != 0) +@@ -1723,6 +1803,9 @@ int keystore_change_key(struct keystore + } + } + ++ rc = _keystore_ensure_vp_exists(keystore, &file_names, key_props); ++ /* ignore return code, vp generation might fail if key is not valid */ ++ + rc = _keystore_set_timestamp_property(key_props, PROP_NAME_CHANGE_TIME); + if (rc != 0) + goto out; +@@ -1838,7 +1921,7 @@ static struct util_rec *_keystore_setup_ + { + struct util_rec *rec; + +- rec = util_rec_new_long("-", ":", REC_KEY, 23, 54); ++ rec = util_rec_new_long("-", ":", REC_KEY, 28, 54); + util_rec_def(rec, REC_KEY, UTIL_REC_ALIGN_LEFT, 54, REC_KEY); + if (validation) + util_rec_def(rec, REC_STATUS, UTIL_REC_ALIGN_LEFT, 54, +@@ -1858,6 +1941,7 @@ static struct util_rec *_keystore_setup_ + util_rec_def(rec, REC_KEY_FILE, UTIL_REC_ALIGN_LEFT, 54, REC_KEY_FILE); + util_rec_def(rec, REC_SECTOR_SIZE, UTIL_REC_ALIGN_LEFT, 54, + REC_SECTOR_SIZE); ++ util_rec_def(rec, REC_KEY_VP, UTIL_REC_ALIGN_LEFT, 54, REC_KEY_VP); + util_rec_def(rec, REC_CREATION_TIME, UTIL_REC_ALIGN_LEFT, 54, + REC_CREATION_TIME); + util_rec_def(rec, REC_CHANGE_TIME, UTIL_REC_ALIGN_LEFT, 54, +@@ -1876,6 +1960,7 @@ static void _keystore_print_record(struc + size_t clear_key_bitsize, bool valid, + bool is_old_mk, bool reenc_pending) + { ++ char temp_vp[VERIFICATION_PATTERN_LEN + 2]; + char *volumes_argz = NULL; + size_t volumes_argz_len; + char *apqns_argz = NULL; +@@ -1888,6 +1973,8 @@ static void _keystore_print_record(struc + char *change; + char *apqns; + char *temp; ++ char *vp; ++ int len; + + description = properties_get(properties, PROP_NAME_DESCRIPTION); + volumes = properties_get(properties, PROP_NAME_VOLUMES); +@@ -1913,6 +2000,7 @@ static void _keystore_print_record(struc + creation = properties_get(properties, PROP_NAME_CREATION_TIME); + change = properties_get(properties, PROP_NAME_CHANGE_TIME); + reencipher = properties_get(properties, PROP_NAME_REENC_TIME); ++ vp = properties_get(properties, PROP_NAME_KEY_VP); + + util_rec_set(rec, REC_KEY, name); + if (validation) +@@ -1951,6 +2039,15 @@ static void _keystore_print_record(struc + else + util_rec_set(rec, REC_SECTOR_SIZE, "%lu bytes", + sector_size); ++ if (vp != NULL) { ++ len = sprintf(temp_vp, "%.*s%c%.*s", ++ VERIFICATION_PATTERN_LEN / 2, vp, ++ '\0', VERIFICATION_PATTERN_LEN / 2, ++ &vp[VERIFICATION_PATTERN_LEN / 2]); ++ util_rec_set_argz(rec, REC_KEY_VP, temp_vp, len + 1); ++ } else { ++ util_rec_set(rec, REC_KEY_VP, "(not available)"); ++ } + util_rec_set(rec, REC_CREATION_TIME, creation); + util_rec_set(rec, REC_CHANGE_TIME, + change != NULL ? change : "(never)"); +@@ -1976,6 +2073,8 @@ static void _keystore_print_record(struc + free(change); + if (reencipher != NULL) + free(reencipher); ++ if (vp != NULL) ++ free(vp); + } + + struct validate_info { +@@ -2404,6 +2503,17 @@ static int _keystore_process_reencipher( + if (rc != 0) + goto out; + ++ rc = _keystore_ensure_vp_exists(keystore, file_names, ++ properties); ++ if (rc != 0) { ++ warnx("Failed to generate the key verification pattern " ++ "for key '%s': %s", file_names->skey_filename, ++ strerror(-rc)); ++ warnx("Make sure that kernel module 'paes_s390' is loaded and " ++ "that the 'paes' cipher is available"); ++ goto out; ++ } ++ + rc = properties_save(properties, file_names->info_filename, 1); + if (rc != 0) { + pr_verbose(keystore, +@@ -3040,7 +3150,7 @@ static int _keystore_process_crypttab(st + "At the time this utility was developed, systemd's " + "support of crypttab did not support to specify a " + "sector size with plain dm-crypt devices. The generated " +- "crypttab entry may or may not work, and may need " ++ "crypttab entry might or might not work, and might need " + "manual adoptions.", volume, sector_size); + util_print_indented(temp, 0); + } +--- a/zkey/zkey.1 ++++ b/zkey/zkey.1 +@@ -361,8 +361,8 @@ The + command displays the attributes of the secure keys, such as key sizes, + whether it is a secure key that can be used for the XTS cipher mode, the textual + description, associated cryptographic adapters (APQNs) and volumes, the +-sector size, and timestamps for key creation, last modification and last +-re-encipherment. ++sector size, the key verification pattern, and timestamps for key creation, last ++modification and last re-encipherment. + . + .SS "Remove existing AES secure keys from the secure key repository" + . +--- a/zkey/zkey.c ++++ b/zkey/zkey.c +@@ -1057,6 +1057,7 @@ static int command_reencipher(void) + */ + static int command_validate_file(void) + { ++ char vp[VERIFICATION_PATTERN_LEN]; + size_t secure_key_size; + size_t clear_key_size; + u8 *secure_key; +@@ -1089,14 +1090,30 @@ static int command_validate_file(void) + 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", ++ 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(" Encrypted with: %s CCA master key\n", ++ 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); diff --git a/s390-tools-sles15sp1-0019-zkey-Add-volume-type-property-to-support-LUKS2-volum.patch b/s390-tools-sles15sp1-0019-zkey-Add-volume-type-property-to-support-LUKS2-volum.patch new file mode 100644 index 0000000..fa876db --- /dev/null +++ b/s390-tools-sles15sp1-0019-zkey-Add-volume-type-property-to-support-LUKS2-volum.patch @@ -0,0 +1,1803 @@ +Subject: zkey: Add volume-type property to support LUKS2 volumes +From: Ingo Franzki + +Summary: zkey: Support CCA master key change with LUKS2 volumes using paes +Description: Support the usage of protected key crypto for dm-crypt disks in + LUKS2 format by providing a tool allowing to re-encipher a + secure LUKS2 volume key when the CCA master key is changed +Upstream-ID: 1f07a41d5a408c1650d20a688cf10bd02a8e7dd7 +Problem-ID: SEC1424.1 + +Upstream-Description: + + zkey: Add volume-type property to support LUKS2 volumes + + Allow to specify a volume-type for a key. This applies to all + associated volumes. The volume type can be either 'plain' or + 'luks2'. New keys created will default to 'luks2', but existing + keys that do not have a volume-type property default to 'plain' + for compatibility reasons. + + The volume type 'luks2' is only available when the define + HAVE_LUKS2_SUPPORT is set in the makefile. This is set only + when libcryptsetup version 2.0.3 or newer is available + at build time. If the define is not set, the volume-type + option is not available to the user, and the volume-type of + a key defaults to 'plain'. + + Signed-off-by: Ingo Franzki + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Ingo Franzki +--- + zkey/Makefile | 1 + zkey/keystore.c | 423 +++++++++++++++++++++++++++++++++++++++++++++----------- + zkey/keystore.h | 15 + + zkey/zkey.1 | 292 ++++++++++++++++++++++++-------------- + zkey/zkey.c | 100 +++++++++++-- + 5 files changed, 627 insertions(+), 204 deletions(-) + +--- a/zkey/Makefile ++++ b/zkey/Makefile +@@ -12,6 +12,7 @@ ifneq (${HAVE_CRYPTSETUP2},0) + ifneq (${HAVE_JSONC},0) + BUILD_TARGETS += zkey-cryptsetup + INSTALL_TARGETS += install-zkey-cryptsetup ++ CPPFLAGS += -DHAVE_LUKS2_SUPPORT + else + BUILD_TARGETS += zkey-cryptsetup-skip-jsonc + INSTALL_TARGETS += zkey-cryptsetup-skip-jsonc +--- a/zkey/keystore.c ++++ b/zkey/keystore.c +@@ -59,6 +59,15 @@ struct key_filenames { + #define PROP_NAME_CHANGE_TIME "update-time" + #define PROP_NAME_REENC_TIME "reencipher-time" + #define PROP_NAME_KEY_VP "verification-pattern" ++#define PROP_NAME_VOLUME_TYPE "volume-type" ++ ++#define VOLUME_TYPE_PLAIN "plain" ++#define VOLUME_TYPE_LUKS2 "luks2" ++#ifdef HAVE_LUKS2_SUPPORT ++ #define DEFAULT_VOLUME_TYPE VOLUME_TYPE_LUKS2 ++#else ++ #define DEFAULT_VOLUME_TYPE VOLUME_TYPE_PLAIN ++#endif + + #define IS_XTS(secure_key_size) (secure_key_size > SECURE_KEY_SIZE ? 1 : 0) + +@@ -72,11 +81,12 @@ struct key_filenames { + #define REC_KEY_FILE "Key file name" + #define REC_SECTOR_SIZE "Sector size" + #define REC_STATUS "Status" +-#define REC_MASTERKEY "Encrypted with" ++#define REC_MASTERKEY "Enciphered with" + #define REC_CREATION_TIME "Created" + #define REC_CHANGE_TIME "Changed" + #define REC_REENC_TIME "Re-enciphered" + #define REC_KEY_VP "Verification pattern" ++#define REC_VOLUME_TYPE "Volume type" + + #define pr_verbose(keystore, fmt...) do { \ + if (keystore->verbose) \ +@@ -280,6 +290,85 @@ static int _keystore_valid_sector_size(s + return 1; + } + ++/** ++ * Checks if the volume type is supported. ++ * ++ * @param[in] volume_type the volume type ++ * ++ * @returns 1 if the volume type is valid, 0 otherwise ++ */ ++static int _keystore_valid_volume_type(const char *volume_type) ++{ ++ if (strcasecmp(volume_type, VOLUME_TYPE_PLAIN) == 0) ++ return 1; ++#ifdef HAVE_LUKS2_SUPPORT ++ if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) ++ return 1; ++#endif ++ return 0; ++} ++ ++/** ++ * Returns the volume type contained in the properties. If no volume type ++ * property is contained, then 'plain' is assumed (for backward comatibility). ++ * ++ * @returns a string containing the volume type. Must be freed by the caller. ++ */ ++static char *_keystore_get_volume_type(struct properties *properties) ++{ ++ char *type; ++ ++ type = properties_get(properties, PROP_NAME_VOLUME_TYPE); ++ if (type == NULL) ++ type = util_strdup(VOLUME_TYPE_PLAIN); ++ ++ return type; ++} ++ ++/** ++ * Prints a message followed by a list of associated volumes, if volumes are ++ * associated and the volume-type matches (if specified) ++ * ++ * @param[in] msg the message to display ++ * @param[in] properties the properties ++ * @param[in] volume_type the volume type to display the message for (or NULL) ++ * ++ * @returns always zero ++ */ ++static int _keystore_msg_for_volumes(const char *msg, ++ struct properties *properties, ++ const char *volume_type) ++{ ++ char *volumes = NULL; ++ char **volume_list; ++ char *type = NULL; ++ int i; ++ ++ if (volume_type != NULL) { ++ type = _keystore_get_volume_type(properties); ++ if (strcasecmp(type, volume_type) != 0) ++ goto out; ++ } ++ ++ volumes = properties_get(properties, PROP_NAME_VOLUMES); ++ if (volumes != NULL && strlen(volumes) > 0) { ++ volume_list = str_list_split(volumes); ++ ++ util_print_indented(msg, 0); ++ for (i = 0; volume_list[i] != NULL; i++) ++ printf(" %s\n", volume_list[i]); ++ str_list_free_string_array(volume_list); ++ } ++ ++out: ++ if (volumes != NULL) ++ free(volumes); ++ if (type != NULL) ++ free(type); ++ ++ return 0; ++} ++ + typedef int (*check_association_t)(const char *value, bool remove, + char **normalized, void *private); + +@@ -712,6 +801,35 @@ static int _keystore_match_filter_proper + } + + /** ++ * Checks if the volume type property matches the specified volume type. ++ * If the properties do not contain a volume type property, then the default ++ * volume type is assumed. ++ * ++ * @param[in] properties a properties object ++ * @param[in] volume_type the volume type to match. Can be NULL. In this case ++ * it always matches. ++ * ++ * @returns 1 for a match, 0 for not matched ++ */ ++static int _keystore_match_volume_type_property(struct properties *properties, ++ const char *volume_type) ++{ ++ char *type; ++ int rc = 0; ++ ++ if (volume_type == NULL) ++ return 1; ++ ++ type = _keystore_get_volume_type(properties); ++ if (strcasecmp(type, volume_type) == 0) ++ rc = 1; ++ ++ free(type); ++ return rc; ++} ++ ++ ++/** + * Checks if a key name matches a name filter + * + * @param[in] name the name to check +@@ -774,6 +892,7 @@ typedef int (*process_key_t)(struct keys + * @param[in] apqn_filter the APQN filter. Can contain wild cards, and + * mutliple APQN filters separated by commas. + * NULL means no APQN filter. ++ * @param[in] volume_type If not NULL, specifies the volume type. + * @param[in] process_func the callback function called for a matching key + * @param[in/out] process_private private data passed to the process_func + * +@@ -785,6 +904,7 @@ static int _keystore_process_filtered(st + const char *name_filter, + const char *volume_filter, + const char *apqn_filter, ++ const char *volume_type, + process_key_t process_func, + void *process_private) + { +@@ -867,6 +987,15 @@ static int _keystore_process_filtered(st + goto free_prop; + } + ++ rc = _keystore_match_volume_type_property(key_props, ++ volume_type); ++ if (rc == 0) { ++ pr_verbose(keystore, ++ "Key '%s' filtered out due to volume type", ++ name); ++ goto free_prop; ++ } ++ + rc = process_func(keystore, name, key_props, &file_names, + process_private); + if (rc != 0) { +@@ -1122,8 +1251,8 @@ static int _keystore_volume_check(const + } + + rc = _keystore_process_filtered(info->keystore, NULL, info->volume, +- NULL, _keystore_volume_check_process, +- info); ++ NULL, NULL, ++ _keystore_volume_check_process, info); + out: + free((void *)info->volume); + info->volume = NULL; +@@ -1418,13 +1547,15 @@ static int _keystore_set_default_propert + * of two and in range 512 - 4096 bytes. 0 means that + * the sector size is not specified and the system + * default is used. ++ * @param[in] volume_type the type of volume + */ + static int _keystore_create_info_file(struct keystore *keystore, + const char *name, + const struct key_filenames *filenames, + const char *description, + const char *volumes, const char *apqns, +- size_t sector_size) ++ size_t sector_size, ++ const char *volume_type) + { + struct volume_check vol_check = { .keystore = keystore, .name = name }; + struct properties *key_props; +@@ -1469,6 +1600,19 @@ static int _keystore_create_info_file(st + goto out; + } + ++ if (volume_type == NULL) ++ volume_type = DEFAULT_VOLUME_TYPE; ++ if (!_keystore_valid_volume_type(volume_type)) { ++ warnx("Invalid volume-type specified"); ++ rc = -EINVAL; ++ goto out; ++ } ++ rc = properties_set(key_props, PROP_NAME_VOLUME_TYPE, volume_type); ++ if (rc != 0) { ++ warnx("Invalid characters in volume-type"); ++ goto out; ++ } ++ + rc = _keystore_ensure_vp_exists(keystore, filenames, key_props); + if (rc != 0) { + warnx("Failed to generate the key verification pattern: %s", +@@ -1553,6 +1697,7 @@ out: + * @param[in] clear_key_file if not NULL the secure key is generated from the + * clear key contained in the file denoted here. + * if NULL, the secure key is generated by random. ++ * @param[in] volume_type the type of volume + * @param[in] pkey_fd the file descriptor of /dev/pkey + * + * @returns 0 for success or a negative errno in case of an error +@@ -1561,7 +1706,7 @@ int keystore_generate_key(struct keystor + const char *description, const char *volumes, + const char *apqns, size_t sector_size, + size_t keybits, bool xts, const char *clear_key_file, +- int pkey_fd) ++ const char *volume_type, int pkey_fd) + { + struct key_filenames file_names = { NULL, NULL, NULL }; + struct properties *key_props = NULL; +@@ -1603,7 +1748,7 @@ int keystore_generate_key(struct keystor + + rc = _keystore_create_info_file(keystore, name, &file_names, + description, volumes, apqns, +- sector_size); ++ sector_size, volume_type); + if (rc != 0) + goto out_free_props; + +@@ -1640,14 +1785,15 @@ out_free_key_filenames: + * of two and in range 512 - 4096 bytes. 0 means that + * the sector size is not specified and the system + * default is used. +- * @param[in] import_file The name of a secure key containing the kley to import ++ * @param[in] import_file The name of a secure key containing the key to import ++ * @param[in] volume_type the type of volume + * + * @returns 0 for success or a negative errno in case of an error + */ + int keystore_import_key(struct keystore *keystore, const char *name, + const char *description, const char *volumes, + const char *apqns, size_t sector_size, +- const char *import_file) ++ const char *import_file, const char *volume_type) + { + struct key_filenames file_names = { NULL, NULL, NULL }; + struct properties *key_props = NULL; +@@ -1686,7 +1832,7 @@ int keystore_import_key(struct keystore + + rc = _keystore_create_info_file(keystore, name, &file_names, + description, volumes, apqns, +- sector_size); ++ sector_size, volume_type); + if (rc != 0) + goto out_free_props; + +@@ -1722,25 +1868,28 @@ out_free_key_filenames: + * volumes are not changed. + * @param[in] apqns a comma separated list of APQNs associated with this + * key, or an APQN prefixed with '+' or '-' to add or +- * remove that APQN respectively. IfNULL then the APQNs ++ * remove that APQN respectively. If NULL then the APQNs + * are not changed. + * @param[in] sector_size the sector size to use with dm-crypt. It must be power + * of two and in range 512 - 4096 bytes. 0 means that + * the sector size is not specified and the system + * default is used. Specify -1 if this property should + * not be changed. +- * ++ * @param[in] volume_type the type of volume. If NULL then the volume type is ++ * not changed. ++ * * + * @returns 0 for success or a negative errno in case of an error + * + */ + int keystore_change_key(struct keystore *keystore, const char *name, + const char *description, const char *volumes, +- const char *apqns, long int sector_size) ++ const char *apqns, long int sector_size, ++ const char *volume_type) + { + struct volume_check vol_check = { .keystore = keystore, .name = name }; + struct key_filenames file_names = { NULL, NULL, NULL }; + struct properties *key_props = NULL; +- char temp[10]; ++ char temp[30]; + int rc; + + util_assert(keystore != NULL, "Internal error: keystore is NULL"); +@@ -1803,6 +1952,21 @@ int keystore_change_key(struct keystore + } + } + ++ if (volume_type != NULL) { ++ if (!_keystore_valid_volume_type(volume_type)) { ++ warnx("Invalid volume-type specified"); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ rc = properties_set(key_props, PROP_NAME_VOLUME_TYPE, ++ volume_type); ++ if (rc != 0) { ++ warnx("Invalid characters in volume-type"); ++ goto out; ++ } ++ } ++ + rc = _keystore_ensure_vp_exists(keystore, &file_names, key_props); + /* ignore return code, vp generation might fail if key is not valid */ + +@@ -1849,6 +2013,8 @@ int keystore_rename_key(struct keystore + { + struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames new_names = { NULL, NULL, NULL }; ++ struct properties *key_props = NULL; ++ char *msg; + int rc; + + util_assert(keystore != NULL, "Internal error: keystore is NULL"); +@@ -1896,12 +2062,28 @@ int keystore_rename_key(struct keystore + } + } + ++ key_props = properties_new(); ++ rc = properties_load(key_props, new_names.info_filename, 1); ++ if (rc != 0) { ++ warnx("Key '%s' does not exist or is invalid", newname); ++ goto out; ++ } ++ ++ util_asprintf(&msg, "The following volumes are associated with the " ++ "renamed key '%s'. You should adjust the corresponding " ++ "crypttab entries and 'cryptsetup plainOpen' commands to " ++ "use the new name.", newname); ++ _keystore_msg_for_volumes(msg, key_props, VOLUME_TYPE_PLAIN); ++ free(msg); ++ + pr_verbose(keystore, "Successfully renamed key '%s' to '%s'", name, + newname); + + out: + _keystore_free_key_filenames(&file_names); + _keystore_free_key_filenames(&new_names); ++ if (key_props != NULL) ++ properties_free(key_props); + + if (rc != 0) + pr_verbose(keystore, "Failed to rename key '%s'to '%s': %s", +@@ -1941,6 +2123,8 @@ static struct util_rec *_keystore_setup_ + util_rec_def(rec, REC_KEY_FILE, UTIL_REC_ALIGN_LEFT, 54, REC_KEY_FILE); + util_rec_def(rec, REC_SECTOR_SIZE, UTIL_REC_ALIGN_LEFT, 54, + REC_SECTOR_SIZE); ++ util_rec_def(rec, REC_VOLUME_TYPE, UTIL_REC_ALIGN_LEFT, 54, ++ REC_VOLUME_TYPE); + util_rec_def(rec, REC_KEY_VP, UTIL_REC_ALIGN_LEFT, 54, REC_KEY_VP); + util_rec_def(rec, REC_CREATION_TIME, UTIL_REC_ALIGN_LEFT, 54, + REC_CREATION_TIME); +@@ -1967,6 +2151,7 @@ static void _keystore_print_record(struc + size_t sector_size = 0; + size_t apqns_argz_len; + char *description; ++ char *volume_type; + char *reencipher; + char *creation; + char *volumes; +@@ -2001,6 +2186,7 @@ static void _keystore_print_record(struc + change = properties_get(properties, PROP_NAME_CHANGE_TIME); + reencipher = properties_get(properties, PROP_NAME_REENC_TIME); + vp = properties_get(properties, PROP_NAME_KEY_VP); ++ volume_type = _keystore_get_volume_type(properties); + + util_rec_set(rec, REC_KEY, name); + if (validation) +@@ -2039,6 +2225,7 @@ static void _keystore_print_record(struc + else + util_rec_set(rec, REC_SECTOR_SIZE, "%lu bytes", + sector_size); ++ util_rec_set(rec, REC_VOLUME_TYPE, volume_type); + if (vp != NULL) { + len = sprintf(temp_vp, "%.*s%c%.*s", + VERIFICATION_PATTERN_LEN / 2, vp, +@@ -2075,6 +2262,8 @@ static void _keystore_print_record(struc + free(reencipher); + if (vp != NULL) + free(vp); ++ if (volume_type != NULL) ++ free(volume_type); + } + + struct validate_info { +@@ -2227,10 +2416,10 @@ static int _keystore_process_validate(st + + if (valid && is_old_mk) { + util_print_indented("WARNING: The secure key is currently " +- "enciphered with the OLD CCA master key " +- "and should be re-enciphered with the " +- "CURRENT CCA master key as soon as " +- "possible to avoid data loss\n", 0); ++ "enciphered with the OLD CCA master key. " ++ "To mitigate the danger of data loss " ++ "re-encipher it with the CURRENT CCA " ++ "master key\n", 0); + info->num_warnings++; + } + if (_keystore_display_apqn_status(properties, name) != 0) +@@ -2271,7 +2460,7 @@ int keystore_validate_key(struct keystor + info.num_warnings = 0; + + rc = _keystore_process_filtered(keystore, name_filter, NULL, +- apqn_filter, ++ apqn_filter, NULL, + _keystore_process_validate, &info); + + util_rec_free(rec); +@@ -2458,7 +2647,8 @@ static int _keystore_process_reencipher( + if (params.complete) { + warnx("Key '%s' is not valid, re-enciphering is not " + "completed", name); +- warnx("Possibly the CCA master key not yet been set?"); ++ warnx("The new CCA master key might yet have to be set " ++ "as the CURRENT master key."); + } else { + warnx("Key '%s' is not valid, it is not re-enciphered", + name); +@@ -2526,6 +2716,14 @@ static int _keystore_process_reencipher( + file_names->info_filename); + if (rc != 0) + goto out; ++ ++ util_asprintf(&temp, "The following LUKS2 volumes are " ++ "encrypted with key '%s'. You should also " ++ "re-encipher the volume key of those volumes " ++ "using command 'zkey-cryptsetup reencipher " ++ "':", name); ++ _keystore_msg_for_volumes(temp, properties, VOLUME_TYPE_LUKS2); ++ free(temp); + } + + if (params.complete || +@@ -2539,12 +2737,11 @@ static int _keystore_process_reencipher( + } + + if (params.inplace != 1) { +- util_asprintf(&temp, "Staged re-enciphering has completed for " +- "key '%s'. Run 'zkey reencipher' with option " +- "'--complete' when the NEW CCA master key has " +- "been set (moved to the CURRENT master key " +- "register) to complete the re-enciphering " +- "process", name); ++ util_asprintf(&temp, "Staged re-enciphering is initiated for " ++ "key '%s'. After the NEW CCA master key has been " ++ "set to become the CURRENT master key run " ++ "'zkey reencipher' with option '--complete' to " ++ "complete the re-enciphering process", name); + util_print_indented(temp, 0); + free(temp); + } +@@ -2613,7 +2810,7 @@ int keystore_reencipher_key(struct keyst + info.num_skipped = 0; + + rc = _keystore_process_filtered(keystore, name_filter, NULL, +- apqn_filter, ++ apqn_filter, NULL, + _keystore_process_reencipher, &info); + + if (rc != 0) { +@@ -2833,10 +3030,9 @@ static int _keystore_propmp_for_remove(s + struct key_filenames *file_names) + { + struct properties *key_prop; +- char *volumes = NULL; +- char **volume_list = NULL; + char str[20]; +- int rc, i; ++ char *msg; ++ int rc; + + key_prop = properties_new(); + rc = properties_load(key_prop, file_names->info_filename, 1); +@@ -2845,15 +3041,10 @@ static int _keystore_propmp_for_remove(s + goto out; + } + +- volumes = properties_get(key_prop, PROP_NAME_VOLUMES); +- if (volumes != NULL && strlen(volumes) > 0) { +- volume_list = str_list_split(volumes); +- +- warnx("When you remove key '%s' the following volumes will " +- "no longer be usable:", name); +- for (i = 0; volume_list[i] != NULL; i++) +- fprintf(stderr, "%s\n", volume_list[i]); +- } ++ util_asprintf(&msg, "When you remove key '%s' the following volumes " ++ "will no longer be usable:", name); ++ _keystore_msg_for_volumes(msg, key_prop, VOLUME_TYPE_PLAIN); ++ free(msg); + + printf("%s: Remove key '%s'? ", program_invocation_short_name, name); + if (fgets(str, sizeof(str), stdin) == NULL) { +@@ -2870,9 +3061,6 @@ static int _keystore_propmp_for_remove(s + + out: + properties_free(key_prop); +- if (volume_list != NULL) +- str_list_free_string_array(volume_list); +- + return rc; + } + +@@ -3000,22 +3188,30 @@ out: + * @param[in] apqn_filter the APQN filter. Can contain wild cards, and + * mutliple APQN filters separated by commas. + * NULL means no APQN filter. ++ * @param[in] volume_type The volume type. NULL means no volume type filter. + * + * @returns 0 for success or a negative errno in case of an error + */ + int keystore_list_keys(struct keystore *keystore, const char *name_filter, +- const char *volume_filter, const char *apqn_filter) ++ const char *volume_filter, const char *apqn_filter, ++ const char *volume_type) + { + struct util_rec *rec; + int rc; + + util_assert(keystore != NULL, "Internal error: keystore is NULL"); + ++ if (volume_type != NULL && ++ !_keystore_valid_volume_type(volume_type)) { ++ warnx("Invalid volume-type specified"); ++ return -EINVAL; ++ } ++ + rec = _keystore_setup_record(0); + + rc = _keystore_process_filtered(keystore, name_filter, volume_filter, +- apqn_filter, _keystore_display_key, +- rec); ++ apqn_filter, volume_type, ++ _keystore_display_key, rec); + util_rec_free(rec); + + if (rc != 0) +@@ -3067,6 +3263,7 @@ struct crypt_info { + const char *key_file_name, + size_t key_file_size, + size_t sector_size, ++ const char *volume_type, + struct crypt_info *info); + }; + +@@ -3080,7 +3277,8 @@ struct crypt_info { + * @param[in] cipher_spec the cipher specification + * @param[in] key_file_name the key file name + * @param[in] key_file_size the size of the key file in bytes +- * @param sector_size the sector size in bytes or 0 if not specified ++ * @param[in] sector_size the sector size in bytes or 0 if not specified ++ * @param[in] volume_type the volume type + * @param[in] info processing info + * + * @returns 0 if successful, a negative errno value otherwise +@@ -3092,6 +3290,7 @@ static int _keystore_process_cryptsetup( + const char *key_file_name, + size_t key_file_size, + size_t sector_size, ++ const char *volume_type, + struct crypt_info *info) + { + char temp[100]; +@@ -3099,18 +3298,53 @@ static int _keystore_process_cryptsetup( + char *cmd; + + sprintf(temp, "--sector-size %lu ", sector_size); +- util_asprintf(&cmd, +- "cryptsetup plainOpen %s--key-file '%s' --key-size %lu " +- "--cipher %s %s%s %s", +- keystore->verbose ? "-v " : "", key_file_name, +- key_file_size * 8, cipher_spec, +- sector_size > 0 ? temp : "", volume, dmname); +- +- if (info->execute) { +- printf("Executing: %s\n", cmd); +- rc = _keystore_execute_cmd(cmd, "cryptsetup"); ++ ++ if (strcasecmp(volume_type, VOLUME_TYPE_PLAIN) == 0) { ++ util_asprintf(&cmd, ++ "cryptsetup plainOpen %s--key-file '%s' " ++ "--key-size %lu --cipher %s %s%s %s", ++ keystore->verbose ? "-v " : "", key_file_name, ++ key_file_size * 8, cipher_spec, ++ sector_size > 0 ? temp : "", volume, dmname); ++ ++ if (info->execute) { ++ printf("Executing: %s\n", cmd); ++ rc = _keystore_execute_cmd(cmd, "cryptsetup"); ++ } else { ++ printf("%s\n", cmd); ++ } ++ } else if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) { ++ util_asprintf(&cmd, ++ "cryptsetup luksFormat %s--type luks2 " ++ "--master-key-file '%s' --key-size %lu " ++ "--cipher %s %s%s", ++ keystore->verbose ? "-v " : "", key_file_name, ++ key_file_size * 8, cipher_spec, ++ sector_size > 0 ? temp : "", volume); ++ ++ if (info->execute) { ++ printf("Executing: %s\n", cmd); ++ rc = _keystore_execute_cmd(cmd, "cryptsetup"); ++ } else { ++ printf("%s\n", cmd); ++ } ++ ++ free(cmd); ++ if (rc != 0) ++ return rc; ++ ++ util_asprintf(&cmd, ++ "zkey-cryptsetup setvp %s%s", volume, ++ keystore->verbose ? " -V " : ""); ++ ++ if (info->execute) { ++ printf("Executing: %s\n", cmd); ++ rc = _keystore_execute_cmd(cmd, "zkey-cryptsetup"); ++ } else { ++ printf("%s\n", cmd); ++ } + } else { +- printf("%s\n", cmd); ++ return -EINVAL; + } + + free(cmd); +@@ -3127,7 +3361,8 @@ static int _keystore_process_cryptsetup( + * @param[in] cipher_spec the cipher specification + * @param[in] key_file_name the key file name + * @param[in] key_file_size the size of the key file in bytes +- * @param sector_size the sector size in bytes or 0 if not specified ++ * @param[in] sector_size the sector size in bytes or 0 if not specified ++ * @param[in] volume_type the volume type + * @param[in] info processing info (not used here) + * + * @returns 0 if successful, a negative errno value otherwise +@@ -3140,26 +3375,35 @@ static int _keystore_process_crypttab(st + const char *key_file_name, + size_t key_file_size, + size_t sector_size, ++ const char *volume_type, + struct crypt_info *UNUSED(info)) + { + char temp[1000]; + +- if (sector_size > 0) { +- sprintf(temp, +- "WARNING: volume '%s' is using a sector size of %lu. " +- "At the time this utility was developed, systemd's " +- "support of crypttab did not support to specify a " +- "sector size with plain dm-crypt devices. The generated " +- "crypttab entry might or might not work, and might need " +- "manual adoptions.", volume, sector_size); +- util_print_indented(temp, 0); ++ if (strcasecmp(volume_type, VOLUME_TYPE_PLAIN) == 0) { ++ if (sector_size > 0) { ++ sprintf(temp, ++ "WARNING: volume '%s' is using a sector size " ++ "of %lu. At the time this utility was " ++ "developed, systemd's support of crypttab did " ++ "not support to specify a sector size with " ++ "plain dm-crypt devices. The generated " ++ "crypttab entry might or might not work, and " ++ "might need manual adoptions.", volume, ++ sector_size); ++ util_print_indented(temp, 0); ++ } ++ ++ sprintf(temp, ",sector-size=%lu", sector_size); ++ printf("%s\t%s\t%s\tplain,cipher=%s,size=%lu,hash=plain%s\n", ++ dmname, volume, key_file_name, cipher_spec, ++ key_file_size * 8, sector_size > 0 ? temp : ""); ++ } else if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) { ++ printf("%s\t%s\n", dmname, volume); ++ } else { ++ return -EINVAL; + } + +- sprintf(temp, ",sector-size=%lu", sector_size); +- printf("%s\t%s\t%s\tplain,cipher=%s,size=%lu,hash=plain%s\n", +- dmname, volume, key_file_name, cipher_spec, key_file_size * 8, +- sector_size > 0 ? temp : ""); +- + return 0; + } + +@@ -3251,6 +3495,7 @@ static int _keystore_process_crypt(struc + struct crypt_info *info = (struct crypt_info *)private; + char **volume_list = NULL; + char *cipher_spec = NULL; ++ char *volume_type = NULL; + size_t secure_key_size; + size_t sector_size = 0; + char *volumes = NULL; +@@ -3290,6 +3535,8 @@ static int _keystore_process_crypt(struc + free(temp); + } + ++ volume_type = _keystore_get_volume_type(properties); ++ + for (i = 0; volume_list[i] != NULL && rc == 0; i++) { + vol = volume_list[i]; + if (_keystore_match_filter(vol, info->volume_filter, +@@ -3306,7 +3553,8 @@ static int _keystore_process_crypt(struc + + rc = info->process_func(keystore, vol, dmname, + cipher_spec, file_names->skey_filename, +- secure_key_size, sector_size, info); ++ secure_key_size, sector_size, ++ volume_type, info); + if (rc != 0) + break; + } +@@ -3319,6 +3567,8 @@ out: + str_list_free_string_array(volume_list); + if (cipher_spec != NULL) + free(cipher_spec); ++ if (volume_type != NULL) ++ free(volume_type); + return rc; + } + +@@ -3333,11 +3583,12 @@ out: + * checks the volume part only. + * @param[in] execute If TRUE the cryptsetup command is executed, + * otherwise it is printed to stdout +- * ++ * @param[in] volume_type the type of volume to generate cryptsetup cmds for ++ * * + * @returns 0 for success or a negative errno in case of an error + */ + int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, +- bool execute) ++ bool execute, const char *volume_type) + { + struct crypt_info info = { 0 }; + int rc; +@@ -3346,12 +3597,20 @@ int keystore_cryptsetup(struct keystore + + if (volume_filter == NULL) + volume_filter = "*"; ++ ++ if (volume_type != NULL && ++ !_keystore_valid_volume_type(volume_type)) { ++ warnx("Invalid volume-type specified"); ++ return -EINVAL; ++ } ++ + info.execute = execute; + info.volume_filter = str_list_split(volume_filter); + info.process_func = _keystore_process_cryptsetup; + + rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL, +- _keystore_process_crypt, &info); ++ volume_type, _keystore_process_crypt, ++ &info); + + str_list_free_string_array(info.volume_filter); + +@@ -3376,10 +3635,12 @@ int keystore_cryptsetup(struct keystore + * The ':dm-name' part of the volume is optional + * for the volume filter. If not specified, the filter + * checks the volume part only. ++ * @param[in] volume_type the type of volume to generate crypttab entries for + * + * @returns 0 for success or a negative errno in case of an error + */ +-int keystore_crypttab(struct keystore *keystore, const char *volume_filter) ++int keystore_crypttab(struct keystore *keystore, const char *volume_filter, ++ const char *volume_type) + { + struct crypt_info info = { 0 }; + int rc; +@@ -3388,11 +3649,19 @@ int keystore_crypttab(struct keystore *k + + if (volume_filter == NULL) + volume_filter = "*"; ++ ++ if (volume_type != NULL && ++ !_keystore_valid_volume_type(volume_type)) { ++ warnx("Invalid volume-type specified"); ++ return -EINVAL; ++ } ++ + info.volume_filter = str_list_split(volume_filter); + info.process_func = _keystore_process_crypttab; + + rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL, +- _keystore_process_crypt, &info); ++ volume_type, _keystore_process_crypt, ++ &info); + + str_list_free_string_array(info.volume_filter); + +--- a/zkey/keystore.h ++++ b/zkey/keystore.h +@@ -30,16 +30,17 @@ int keystore_generate_key(struct keystor + const char *description, const char *volumes, + const char *apqns, size_t sector_size, + size_t keybits, bool xts, const char *clear_key_file, +- int pkey_fd); ++ const char *volume_type, int pkey_fd); + + int keystore_import_key(struct keystore *keystore, const char *name, + const char *description, const char *volumes, + const char *apqns, size_t sector_size, +- const char *import_file); ++ const char *import_file, const char *volume_type); + + int keystore_change_key(struct keystore *keystore, const char *name, + const char *description, const char *volumes, +- const char *apqns, long int sector_size); ++ const char *apqns, long int sector_size, ++ const char *volume_type); + + int keystore_rename_key(struct keystore *keystore, const char *name, + const char *newname); +@@ -63,12 +64,14 @@ int keystore_remove_key(struct keystore + bool quiet); + + int keystore_list_keys(struct keystore *keystore, const char *name_filter, +- const char *volume_filter, const char *apqn_filter); ++ const char *volume_filter, const char *apqn_filter, ++ const char *volume_type); + + int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter, +- bool execute); ++ bool execute, const char *volume_type); + +-int keystore_crypttab(struct keystore *keystore, const char *volume_filter); ++int keystore_crypttab(struct keystore *keystore, const char *volume_filter, ++ const char *volume_type); + + void keystore_free(struct keystore *keystore); + +--- a/zkey/zkey.1 ++++ b/zkey/zkey.1 +@@ -75,30 +75,32 @@ key repository. + .BR generate | gen + .I secure\-key\-file + .RB [ \-\-keybits | \-k +-.IB size ] ++.IR size ] + .RB [ \-\-xts | \-x ] + .RB [ \-\-clearkey | \-c +-.IB clear\-key\-file ] ++.IR clear\-key\-file ] + .RB [ \-\-verbose | \-V ] + . + .PP + .B zkey + .BR generate | gen + .B \-\-name | \-N +-.IB key-name ++.IR key-name + .RB [ \-\-description | \-d +-.IB description ] ++.IR description ] + .RB [ \-\-volumes | \-l +-.IB volume1:dmname1[,volume2:dmname2[,...]] ] ++.IR volume1:dmname1[,volume2:dmname2[,...]] ] + .RB [ \-\-apqns | \-a +-.IB card1.domain1[,card2.domain2[,...]] ] ++.IR card1.domain1[,card2.domain2[,...]] ] + .RB [ \-\-sector-size | \-S +-.IB bytes ] ++.IR bytes ] ++.RB [ \-\-volume-type | \-t ++.IR type ] + .RB [ \-\-keybits | \-k +-.IB size ] ++.IR size ] + .RB [ \-\-xts | \-x ] + .RB [ \-\-clearkey | \-c +-.IB clear\-key\-file ] ++.IR clear\-key\-file ] + .RB [ \-\-verbose | \-V ] + .PP + Use the +@@ -115,16 +117,16 @@ The generated secure key can either be s + or in the secure key repository. To store the generated secure key in a + file, specify the file name with option \fIsecure\-key\-file\fP. To store the + secure key in the secure key repository, specify the name of the key using the +-.B --name ++.B \-\-name + option. When storing the secure key in a key repository, + additional information can be associated with a secure key using the +-.B --description ++.B \-\-description + , +-.B --volumes ++.B \-\-volumes + , +-.B --apqns ++.B \-\-apqns + , or the +-.B --sector-size ++.B \-\-sector-size + options. + . + .SS "Validating secure AES keys" +@@ -138,7 +140,7 @@ options. + .B zkey + .BR validate | val + .RB [ \-\-name | \-N +-.IB key-name ] ++.IR key-name ] + .RB [ \-\-verbose | \-V ] + .PP + Use the +@@ -156,10 +158,10 @@ secure key repository. To validate a sec + the file name with option \fIsecure\-key\-file\fP. To validate secure keys + contained in the secure key repository, specify the name of the key + or a pattern containing wildcards using the +-.B --name ++.B \-\-name + option. When wildcards are used you must quote the value. + If neither option \fIsecure\-key\-file\fP nor option +-.B --name ++.B \-\-name + are specified, then all secure keys contained in the key repository + are validated. + . +@@ -171,15 +173,15 @@ are validated. + .RB [ \-\-to\-new | \-n ] + .RB [ \-\-from\-old | \-o ] + .RB [ \-\-output | \-f +-.IB output\-file ] ++.IR output\-file ] + .RB [ \-\-verbose | \-V ] + .PP + .B zkey + .BR reencipher | re + .RB [ \-\-name | \-N +-.IB key-name ] ++.IR key-name ] + .RB [ \-\-apqns | \-a +-.IB card1.domain1[,card2.domain2[,...]] ] ++.IR card1.domain1[,card2.domain2[,...]] ] + .RB [ \-\-to\-new | \-n ] + .RB [ \-\-from\-old | \-o ] + .RB [ \-\-in-place | \-i ] +@@ -190,7 +192,7 @@ are validated. + Use the + .B reencipher + command to re-encipher an existing secure key with a new master key. +-A secure key bust be re-enciphered when the master key of the CCA ++A secure key must be re-enciphered when the master key of the CCA + cryptographic adapter changes. + .PP + The CCA cryptographic adapter has three different registers to store +@@ -243,18 +245,18 @@ secure key repository. To re-encipher a + specify the file name with option \fIsecure\-key\-file\fP. To re-encipher + secure keys contained in the secure key repository, specify the name of the key + or a pattern containing wildcards using the +-.B --name ++.B \-\-name + option. When wildcards are used you must quote the value. + You can also specify the +-.B --apqns ++.B \-\-apqns + option to re-encipher those secure + keys which are associated with the specified cryptographic adapters (APQNs). + You can use wildcards for the APQN specification. + When wildcards are used you must quote the value. + If both option +-.B --name ++.B \-\-name + and option +-.B --apqns ++.B \-\-apqns + are specified then all secure keys + contained in the key repository that match both patterns are re-enciphered. + If all both options are omitted, then all secure keys contained in the key +@@ -265,7 +267,7 @@ performed \fBin-place\fP, or in \fBstage + .PP + \fB"In-place"\fP immediately replaces the secure key in the repository with + the re-enciphered secure key. Re-enciphering from \fBOLD\fP to \fBCURRENT\fP is +-performed in-place per default. You can use option \fB--in-place\fP to force an ++performed in-place per default. You can use option \fB\-\-in-place\fP to force an + in-place re-enciphering for the \fBCURRENT\fP to \fBNEW\fP case. Be aware that + a secure key that was re-enciphered in-place from \fBCURRENT\fP to \fBNEW\fP + is no longer valid, until the new CCA master key has been made the current one. +@@ -273,9 +275,9 @@ is no longer valid, until the new CCA ma + \fBStaged\fP mode means that the re-enciphered secure key is stored in a + separate file in the secure key repository. Thus the current secure key is still + valid at this point. Once the new CCA master key has been set (made active), you +-must rerun the reencipher command with option \fB--complete\fP to complete the ++must rerun the reencipher command with option \fB\-\-complete\fP to complete the + staged re-enciphering. Re-enciphering from \fBCURRENT\fP to \fBNEW\fP is +-performed in staged mode per default. You can use option \fB--staged\fP to force ++performed in staged mode per default. You can use option \fB\-\-staged\fP to force + a staged re-enciphering for the \fBOLD\fP to \fBCURRENT\fP case. + .PP + .B Note: +@@ -288,15 +290,17 @@ to be installed. + .BR import | im + .I secure\-key\-file + .B \-\-name | \-N +-.IB key-name ++.IR key-name + .RB [ \-\-description | \-d +-.IB description ] ++.IR description ] + .RB [ \-\-volumes | \-l +-.IB volume1:dmname1[,volume2:dmname2[,...]] ] ++.IR volume1:dmname1[,volume2:dmname2[,...]] ] + .RB [ \-\-apqns | \-a +-.IB card1.domain1[,card2.domain2[,...]] ] ++.IR card1.domain1[,card2.domain2[,...]] ] + .RB [ \-\-sector-size | \-S +-.IB bytes ] ++.IR bytes ] ++.RB [ \-\-volume-type | \-t ++.IR type ] + .RB [ \-\-verbose | \-V ] + . + .PP +@@ -305,13 +309,13 @@ Use the + command to import an existing secure key contained in a file into the the + secure key repository. When importing a secure key in a key repository, + additional information can be associated with a secure key using the +-.B --description ++.B \-\-description + , +-.B --volumes ++.B \-\-volumes + , +-.B --apqns ++.B \-\-apqns + , or the +-.B --sector-size ++.B \-\-sector-size + options. + . + .SS "Export AES secure keys from the secure key repository" +@@ -320,7 +324,7 @@ options. + .BR export | ex + .I secure\-key\-file + .B \-\-name | \-N +-.IB key-name ++.IR key-name + .RB [ \-\-verbose | \-V ] + . + .PP +@@ -329,7 +333,7 @@ Use the + command to export an existing secure key contained in the secure key repository + to a file in the file system. Specify the name of the key that is to be exported + using the +-.B --name ++.B \-\-name + option. You cannot use wildcards. + When wildcards are used you must quote the value. + The exported secure key also remains in the secure key repository. +@@ -339,20 +343,22 @@ The exported secure key also remains in + .B zkey + .BR list | li + .RB [ \-\-name | \-N +-.IB key-name ] ++.IR key-name ] + .RB [ \-\-volumes | \-l +-.IB volume1[:dmname1][,volume2[:dmname2][,...]] ] ++.IR volume1[:dmname1][,volume2[:dmname2][,...]] ] + .RB [ \-\-apqns | \-a +-.IB card1.domain1[,card2.domain2[,...]] ] ++.IR card1.domain1[,card2.domain2[,...]] ] ++.RB [ \-\-volume-type | \-t ++.IR type ] + .RB [ \-\-verbose | \-V ] + . + .PP + Use the + .B list + command to display a list of secure keys contained in the secure key repository. +-You can filter the displayed list by key name, associated volumes, and +-associated cryptographic adapters (APQNs). You can use wildcards for the key +-name, associated APQNs, and associated volumes. The device-mapper name of an ++You can filter the displayed list by key name, associated volumes, associated ++cryptographic adapters (APQNs), and volume type. You can use wildcards for the ++key name, associated APQNs, and associated volumes. The device-mapper name of an + associated volume can be omitted; if it is specified then only those keys are + listed that are associated with the specified volume and device-mapper name. + .PP +@@ -369,7 +375,7 @@ modification and last re-encipherment. + .B zkey + .BR remove | rem + .B \-\-name | \-N +-.IB key-name ++.IR key-name + .RB [ \-\-force | \-F ] + .RB [ \-\-verbose | \-V ] + . +@@ -378,10 +384,10 @@ Use the + .B remove + command to remove an existing secure key from the secure key repository. + Specify the name of the key that is to be removed using the +-.B --name ++.B \-\-name + option. You cannot use wildcards. The remove command prompts for + a confirmation, unless you specify the +-.B --force ++.B \-\-force + option. + .PP + .B Note: +@@ -395,43 +401,45 @@ secure key. + .B zkey + .BR change | ch + .B \-\-name | \-N +-.IB key-name ++.IR key-name + .RB [ \-\-description | \-d +-.IB description ] ++.IR description ] + .RB [ \-\-volumes | \-l +-.IB [+|-]volume1:dmname1[,volume2:dmname2[,...]] ] ++.IR [+|-]volume1:dmname1[,volume2:dmname2[,...]] ] + .RB [ \-\-apqns | \-a +-.IB [+|-]card1.domain1[,card2.domain2[,...]] ] ++.IR [+|-]card1.domain1[,card2.domain2[,...]] ] + .RB [ \-\-sector-size | \-S +-.IB bytes ] ++.IR bytes ] ++.RB [ \-\-volume-type | \-t ++.IR type ] + .RB [ \-\-verbose | \-V ] + . + .PP + Use the + .B change + command to change the description, the associated volumes, the associated +-cryptographic adapters (APQNs), and the sector size of a secure key contained +-in the secure key repository. Specify the name of the key that is to be changed +-using the +-.B --name ++cryptographic adapters (APQNs), the sector size, and the volume type of a secure ++key contained in the secure key repository. Specify the name of the key that is ++to be changed using the ++.B \-\-name + option. You cannot use wildcards. + .PP + You can set (replace), add, or + remove volume and cryptographic adapters (APQN) associations. To set + (replace) an association, specify the association with the +-.B --volumes ++.B \-\-volumes + or the +-.B --apqns ++.B \-\-apqns + options. To add an association, + specify the new association prefixed with a \fI+\fP with the +-.B --volumes ++.B \-\-volumes + or the +-.B --apqns ++.B \-\-apqns + options. To remove an association, + specify the association to remove prefixed with a \fI-\fP with the +-.B --volumes ++.B \-\-volumes + or the +-.B --apqns ++.B \-\-apqns + options. You cannot mix \fI+\fP and + \fI-\fP in one specification. You can either add or remove (or set) the + associations with one command. +@@ -447,9 +455,9 @@ command. + .B zkey + .BR rename | ren + .B \-\-name | \-N +-.IB key-name ++.IR key-name + .B \-\-new-name | \-w +-.IB new-key-name ++.IR new-key-name + .RB [ \-\-verbose | \-V ] + . + .PP +@@ -457,9 +465,9 @@ Use the + .B rename + command to rename an existing secure key in the secure key repository. + Specify the name of the key that is to be renamed using the +-.B --name ++.B \-\-name + option and the new name using the +-.B --new-name ++.B \-\-new-name + option. You cannot use wildcards. + . + .SS "Copy (duplicate) existing AES secure keys in the secure key repository" +@@ -467,11 +475,11 @@ option. You cannot use wildcards. + .B zkey + .B copy | co + .RB \-\-name | \-N +-.IB key-name ++.IR key-name + .B \-\-new-key-name | \-w +-.IB new-name ++.IR new-name + .RB [ \-\-volumes | \-l +-.IB volume1:dmname1[,volume2:dmname2[,...]] ] ++.IR volume1:dmname1[,volume2:dmname2[,...]] ] + .RB [ \-\-verbose | \-V ] + . + .PP +@@ -479,15 +487,15 @@ Use the + .B copy + command to copy (duplicate) an existing secure key in the secure key repository. + Specify the name of the key that is to be copied using the +-.B --name ++.B \-\-name + option and the name of the copied key using the +-.B --new-name ++.B \-\-new-name + option. You cannot use wildcards. + .PP + .B Note: + When copying a secure key, the volume associations are not copied, because + a specific volume can only be associated with a single secure key. Specify the +-.B --volumes ++.B \-\-volumes + option to associate different + volumes with the copied secure key, or use the \fBchange\fP command to associate + volumes afterwards. +@@ -497,45 +505,56 @@ volumes afterwards. + .B zkey + .BR crypttab | cryptt + .RB [ \-\-volumes | \-l +-.IB volume1[:dmname1][,volume2[:dmname2][,...]] ] ++.IR volume1[:dmname1][,volume2[:dmname2][,...]] ] ++.RB [ \-\-volume-type | \-t ++.IR type ] + .RB [ \-\-verbose | \-V ] + . + .PP + Use the + .B crypttab +-command to generate crypttab entries using the \fBplain\fP dm-crypt mode +-for volumes that are associated with secure keys contained in the secure key +-repository. Specify the +-.B --volumes ++command to generate crypttab entries using the \fBplain\fP or \fBLUKS2\fP ++dm-crypt mode for volumes that are associated with secure keys contained in the ++secure key repository. Specify the ++.B \-\-volumes + option to limit the list + of volumes where crypttab entries are generated for. You can use wildcards. + When wildcards are used you must quote the value. + The device-mapper name of an associated volume can be omitted; if it is + specified then only those volumes with the specified volume and device-mapper + name are selected. ++Specify the ++.B \-\-volume-type ++option to generate crypttab entries for the specified volume type only. + . + .SS "Generate cryptsetup commands for volumes associated with secure AES keys" + . + .B zkey + .BR cryptsetup | crypts + .RB [ \-\-volumes | \-l +-.IB volume1[:dmname1][,volume2[:dmname2][,...]] ] ++.IR volume1[:dmname1][,volume2[:dmname2][,...]] ] ++.RB [ \-\-volume-type | \-t ++.IR type ] + .RB [ \-\-run | \-r ] + .RB [ \-\-verbose | \-V ] + . + .PP + Use the + .B cryptsetup +-command to generate \fBcryptsetup plainOpen\fP commands for volumes that are +-associated with secure keys contained in the secure key repository. Specify the +-.B --volumes ++command to generate \fBcryptsetup plainOpen\fP or \fBcryptsetup luksFormat\fP ++commands for volumes that are associated with secure keys contained in the ++secure key repository. Specify the ++.B \-\-volumes + option to limit the list + of volumes where cryptsetup commands are generated for. You can use wildcards. + When wildcards are used you must quote the value. + The device-mapper name of an associated volume can be omitted; if it is + specified then only those volumes with the specified volume and device-mapper + name are selected. Specify the +-.B --run ++.B \-\-volume-type ++option to generate cryptsetup commands for the specified volume type only. ++Specify the ++.B \-\-run + option to run the generated cryptsetup commands. + . + . +@@ -589,8 +608,17 @@ This option is only used for secure keys + .TP + .BR \-S ", " \-\-sector-size\~\fIbytes\fP + Specifies the sector size in bytes used with dm-crypt. It must be a power of two +-and in the range 512 - 4096 bytes. If omitted, the system default sector size +-is used. ++and in the range of 512 to 4096 bytes. If omitted, the system default sector ++size is used. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-t ", " \-\-volume-type\~\fItype\fP ++Specifies the volume type of the associated volumes used with dm-crypt. Possible ++values are \fBplain\fP and \fBluks2\fP. If omitted, \fBluks2\fP is used. ++This option is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. If LUKS2 support is not enabled, ++the default volume type is \fBplain\fP. + This option is only used for secure keys contained in the secure key repository. + . + . +@@ -650,7 +678,7 @@ repository is performed in staged mode. + secure key is stored in a separate file in the secure key repository. Thus the + current secure key is still valid at this point. Once the new CCA master key has + been set (made active), you must rerun the reencipher command with option +-\fB--complete\fP to complete the staged re-enciphering. ++\fB\-\-complete\fP to complete the staged re-enciphering. + Re-enciphering from CURRENT to NEW is performed in staged mode per default. + This option is only used for secure keys contained in the secure key repository. + .TP +@@ -690,8 +718,17 @@ This option is only used for secure keys + .TP + .BR \-S ", " \-\-sector-size\~\fIbytes\fP + Specifies the sector size in bytes used with dm-crypt. It must be a power of two +-and in the range 512 - 4096 bytes. If omitted, the system default sector size +-is used. ++and in the range of 512 to 4096 bytes. If omitted, the system default sector ++size is used. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-t ", " \-\-volume-type\~\fItype\fP ++Specifies the volume type of the associated volumes used with dm-crypt. Possible ++values are \fBplain\fP and \fBluks2\fP. If omitted, \fBluks2\fP is used. ++This option is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. If LUKS2 support is not enabled, ++the default volume type is \fBplain\fP. + This option is only used for secure keys contained in the secure key repository. + . + . +@@ -734,6 +771,15 @@ APQNs. Each APQN association specifies a + by a period (like lszcrypt displays it). You can use wildcards in the APQN + specification. + This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-t ", " \-\-volume-type\~\fItype\fP ++Specifies the volume type of the associated volumes used with dm-crypt. Possible ++values are \fBplain\fP and \fBluks2\fP. Only keys with the specified volume ++type are listed. ++This option is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. ++This option is only used for secure keys contained in the secure key repository. + . + . + . +@@ -791,9 +837,16 @@ This option is only used for secure keys + .TP + .BR \-S ", " \-\-sector-size\~\fIbytes\fP + Specifies the sector size in bytes used with dm-crypt. It must be a power of two +-and in the range 512 - 4096 bytes. If omitted, the system default sector size +-is used. Specify \fI0\fP to un-set the sector size so that the system default +-is used. ++and in the range of 512 to 4096 bytes. Specify \fI0\fP to set the sector size ++to the system default. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-t ", " \-\-volume-type\~\fItype\fP ++Specifies the volume type of the associated volumes used with dm-crypt. Possible ++values are \fBplain\fP and \fBluks2\fP. ++This option is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. + This option is only used for secure keys contained in the secure key repository. + . + . +@@ -845,6 +898,15 @@ specified volume and device-mapper name. + the volumes and device-mapper names. + When wildcards are used you must quote the value. + This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-t ", " \-\-volume-type\~\fItype\fP ++Specifies the volume type of the associated volumes used with dm-crypt. Possible ++values are \fBplain\fP and \fBluks2\fP. Only keys with the specified volume ++type are selected to generate crypttab entries for. ++This option is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. ++This option is only used for secure keys contained in the secure key repository. + . + . + . +@@ -861,10 +923,18 @@ the volumes and device-mapper names. + When wildcards are used you must quote the value. + This option is only used for secure keys contained in the secure key repository. + .TP +-.BR \-r ", " \-\-run\fP +-Runs the generated cryptsetup commands. When an execution of a cryptsetup +-command fails, no further cryptsetup commands are executed, and zkey ends +-with an error. ++.BR \-t ", " \-\-volume-type\~\fItype\fP ++Specifies the volume type of the associated volumes used with dm-crypt. Possible ++values are \fBplain\fP and \fBluks2\fP. Only keys with the specified volume ++type are selected to generate cryptsetup commands for. ++This option is only available if ++.B zkey ++has been compiled with LUKS2 support enabled. ++This option is only used for secure keys contained in the secure key repository. ++.TP ++.BR \-r ", " \-\-run ++Runs the generated cryptsetup commands. When one of the cryptsetup command fail, ++no further cryptsetup commands are run, and zkey ends with an error. + This option is only used for secure keys contained in the secure key repository. + . + . +@@ -895,15 +965,20 @@ in file 'seckey.bin'. + Generates a secure AES key from the clear key in file 'clearkey.bin' and + stores it in file 'seckey.bin'. + .TP +-.B zkey generate --name seckey ++.B zkey generate \-\-name seckey + Generates a random 256-bit secure AES key and stores it in the secure key +-repository under the name 'seckey'. ++repository using the name 'seckey'. + .TP +-.B zkey generate --name seckey --volumes /dev/dasdc1:encvol --apqns 03.004c ++.B zkey generate \-\-name seckey \-\-volumes /dev/dasdc1:encvol \-\-apqns 03.004c + Generates a random 256-bit secure AES key and stores it in the secure key +-repository under the name 'seckey' and associates it with block ++repository using the name 'seckey' and associates it with block + device '/dev/dasdc1' and device-mapper name 'encvol', and APQN '03.004c'. + .TP ++.B zkey generate \-\-name seckey \-\-volumes /dev/dasdc1:encvol \-\-volume-type luks2 ++Generates a random 256-bit secure AES key and stores it in the secure key ++repository using the name 'seckey' and associates it with block ++device '/dev/dasdc1' and device-mapper name 'encvol', and a volume type of luks2. ++.TP + .B zkey reencipher seckey.bin \-\-from\-old + Re-enciphers the secure key in file 'seckey.bin' which is currently enciphered + with the master key in the OLD register with the master key in the CURRENT +@@ -915,17 +990,17 @@ Re-enciphers the secure key in file 'sec + with the master key in the CURRENT register with the master key in the NEW + register, and saves the re-enciphered secure key to file 'seckey2.bin'. + .TP +-.B zkey reencipher --name seckey ++.B zkey reencipher \-\-name seckey + Re-enciphers the secure key 'seckey' in the secure key repository. + .TP +-.B zkey reencipher --apqns 03.004c ++.B zkey reencipher \-\-apqns 03.004c + Re-enciphers all secure keys contained in the secure key repository that are + associated with APQN '03.004c'. + .TP + .B zkey validate seckey.bin + Validates the secure key in file 'seckey.bin' and displays its attributes. + .TP +-.B zkey validate --name seckey ++.B zkey validate \-\-name seckey + Validates the secure key 'seckey' in the secure key repository and displays its + attributes. + .TP +@@ -933,25 +1008,28 @@ attributes. + Lists all secure keys in the secure key repository and displays its + attributes. + .TP +-.B zkey list --name '*key' ++.B zkey list \-\-name '*key' + Lists all secure keys in the secure key repository with names ending with 'key' + and displays its attributes. + .TP +-.B zkey change --name seckey --volumes +/dev/dasdc2:encvol2 ++.B zkey change \-\-name seckey \-\-volumes +/dev/dasdc2:encvol2 + Changes the secure key 'seckey' in the secure key repository and adds + volume '/dev/dasdc2' with device-mapper name 'encvol2' to the list of associated + volumes of this secure key. + .TP +-.B zkey change --name seckey --apqns -03.004c ++.B zkey change \-\-name seckey \-\-apqns -03.004c + Changes the secure key 'seckey' in the secure key repository and removes + APQN '03.004c' from the list of associated APQNs of this secure key. + .TP +-.B zkey crypttab --volumes '/dev/dasdc*' ++.B zkey crypttab \-\-volumes '/dev/dasdc*' + Generates crypttab entries for all volumes that match the pattern '/dev/dasdc*'. + .TP +-.B zkey cryptsetup --volumes '*:enc_dasd' ++.B zkey cryptsetup \-\-volumes '*:enc_dasd' + Generates cryptsetup commands for the volumes that uses the device-mapper + name 'enc_dasd'. ++.TP ++.B zkey cryptsetup \-\-volume-type luks2 ++Generates cryptsetup commands for all volumes of type luks2. + . + .SH ENVIRONMENT + .TP +--- a/zkey/zkey.c ++++ b/zkey/zkey.c +@@ -68,6 +68,7 @@ static struct zkey_globals { + char *volumes; + char *apqns; + long int sector_size; ++ char *volume_type; + char *newname; + bool run; + bool force; +@@ -180,6 +181,16 @@ static struct util_opt opt_vec[] = { + "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, +@@ -211,19 +222,23 @@ static struct util_opt opt_vec[] = { + }, + { + .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", ++ .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", ++ .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 a staged re-enchipering of a secure AES key", ++ .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, + }, + { +@@ -310,6 +325,16 @@ static struct util_opt opt_vec[] = { + "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, +@@ -358,6 +383,16 @@ static struct util_opt opt_vec[] = { + "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, +@@ -422,11 +457,19 @@ static struct util_opt opt_vec[] = { + .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", ++ "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, +@@ -494,6 +537,17 @@ static struct util_opt opt_vec[] = { + "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, ++ }, ++#endif + /***********************************************************/ + { + .flags = UTIL_OPT_FLAG_SECTION, +@@ -512,6 +566,17 @@ static struct util_opt opt_vec[] = { + "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", +@@ -819,7 +884,7 @@ static int command_generate_repository(v + + 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); ++ g.clearkeyfile, g.volume_type, g.pkey_fd); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1167,7 +1232,8 @@ static int command_import(void) + g.sector_size = 0; + + rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes, +- g.apqns, g.sector_size, g.pos_arg); ++ g.apqns, g.sector_size, g.pos_arg, ++ g.volume_type); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1200,7 +1266,8 @@ static int command_list(void) + { + int rc; + +- rc = keystore_list_keys(g.keystore, g.name, g.volumes, g.apqns); ++ rc = keystore_list_keys(g.keystore, g.name, g.volumes, g.apqns, ++ g.volume_type); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1239,7 +1306,7 @@ static int command_change(void) + } + + rc = keystore_change_key(g.keystore, g.name, g.description, g.volumes, +- g.apqns, g.sector_size); ++ g.apqns, g.sector_size, g.volume_type); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1299,7 +1366,7 @@ static int command_crypttab(void) + { + int rc; + +- rc = keystore_crypttab(g.keystore, g.volumes); ++ rc = keystore_crypttab(g.keystore, g.volumes, g.volume_type); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1313,7 +1380,7 @@ static int command_cryptsetup(void) + { + int rc; + +- rc = keystore_cryptsetup(g.keystore, g.volumes, g.run); ++ rc = keystore_cryptsetup(g.keystore, g.volumes, g.run, g.volume_type); + + return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } +@@ -1490,6 +1557,11 @@ int main(int argc, char *argv[]) + return EXIT_FAILURE; + } + break; ++#ifdef HAVE_LUKS2_SUPPORT ++ case 't': ++ g.volume_type = optarg; ++ break; ++#endif + case 'w': + g.newname = optarg; + break; diff --git a/s390-tools-sles15sp1-01-cpumf-Add-extended-counter-defintion-files-for-IBM-z.patch b/s390-tools-sles15sp1-01-cpumf-Add-extended-counter-defintion-files-for-IBM-z.patch new file mode 100644 index 0000000..cf3a362 --- /dev/null +++ b/s390-tools-sles15sp1-01-cpumf-Add-extended-counter-defintion-files-for-IBM-z.patch @@ -0,0 +1,361 @@ +Subject: cpumf: Add extended counter defintion files for IBM z14 +From: Hendrik Brueckner + +Summary: cpumf: Add CPU-MF hardware counters for z14 +Description: Add hardware counter definitions for IBM z14. +Upstream-ID: 57f18c5f59766832822a74cc029a8d3b60e3ba0f +Problem-ID: KRN1608 + +Upstream-Description: + + cpumf: Add extended counter defintion files for IBM z14 + + Signed-off-by: Martin Schwidefsky + [brueckner: Prefer plural for counter names] + Signed-off-by: Hendrik Brueckner + Signed-off-by: Stefan Haberland + + +Signed-off-by: Hendrik Brueckner +--- + cpumf/Makefile | 2 + cpumf/bin/cpumf_helper.in | 1 + cpumf/data/cpum-cf-extended-z14.ctr | 303 ++++++++++++++++++++++++++++++++++++ + cpumf/data/cpum-cf-hw-counter.map | 1 + 4 files changed, 306 insertions(+), 1 deletion(-) + +--- a/cpumf/Makefile ++++ b/cpumf/Makefile +@@ -7,7 +7,7 @@ CPUMF_DATADIR = $(TOOLS_DATADIR)/cpumf + DATA_FILES = cpum-cf-hw-counter.map cpum-cf-generic.ctr \ + cpum-cf-extended-z10.ctr cpum-cf-extended-z196.ctr \ + cpum-cf-extended-zEC12.ctr cpum-sf-modes.ctr \ +- cpum-cf-extended-z13.ctr ++ cpum-cf-extended-z13.ctr cpum-cf-extended-z14.ctr + LIB_FILES = bin/cpumf_helper + USRBIN_SCRIPTS = bin/lscpumf + USRSBIN_SCRIPTS = bin/chcpumf +--- a/cpumf/bin/cpumf_helper.in ++++ b/cpumf/bin/cpumf_helper.in +@@ -210,6 +210,7 @@ my $system_z_hwtype_map = { + 2828 => 'IBM zEnterprise BC12', + 2964 => 'IBM z13', + 2965 => 'IBM z13s', ++ 3906 => 'IBM z14', + }; + + sub get_hardware_type() +--- /dev/null ++++ b/cpumf/data/cpum-cf-extended-z14.ctr +@@ -0,0 +1,303 @@ ++# Counter decriptions for the ++# IBM z14 extended counter and MT-diagnostic counter set ++# ++# Notes for transactional-execution mode symbolic names: ++# TX .. transactional-execution mode ++# NC .. nonconstrained ++# C .. constrained ++# ++# Undefined counters in the extended counter set: ++# 142 ++# 158-161 ++# 176-223 ++# 227-231 ++# 233-242 ++# 246-255 ++# Undefined counters in the MT-diagnostic counter set: ++# 450-495 ++# ++# ++# Extended Counter Set ++# --------------------------------------------------------------------- ++Counter:128 Name:L1D_WRITES_RO_EXCL ++A directory write to the Level-1 Data cache where the line was ++originally in a Read-Only state in the cache but has been updated ++to be in the Exclusive state that allows stores to the cache line ++. ++Counter:129 Name:DTLB2_WRITES ++Description: ++A translation has been written into The Translation Lookaside ++Buffer 2 (TLB2) and the request was made by the data cache ++. ++Counter:130 Name:DTLB2_MISSES ++Description: ++A TLB2 miss is in progress for a request made by the data cache. ++Incremented by one for every TLB2 miss in progress for the Level-1 ++Data cache on this cycle ++. ++Counter:131 Name:DTLB2_HPAGE_WRITES ++Description: ++A translation entry was written into the Combined Region and Segment ++Table Entry array in the Level-2 TLB for a one-megabyte page or a ++Last Host Translation was done ++. ++Counter:132 Name:DTLB2_GPAGE_WRITES ++Description: ++A translation entry for a two-gigabyte page was written into the ++Level-2 TLB ++. ++Counter:133 Name:L1D_L2D_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Data cache directory where the ++returned cache line was sourced from the Level-2 Data cache ++. ++Counter:134 Name:ITLB2_WRITES ++Description: ++A translation entry has been written into the Translation Lookaside ++Buffer 2 (TLB2) and the request was made by the instruction cache ++. ++Counter:135 Name:ITLB2_MISSES ++Description: ++A TLB2 miss is in progress for a request made by the instruction cache. ++Incremented by one for every TLB2 miss in progress for the Level-1 ++Instruction cache in a cycle ++. ++Counter:136 Name:L1I_L2I_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache line was sourced from the Level-2 Instruction cache ++. ++Counter:137 Name:TLB2_PTE_WRITES ++Description: ++A translation entry was written into the Page Table Entry array in the ++Level-2 TLB ++. ++Counter:138 Name:TLB2_CRSTE_WRITES ++Description: ++Translation entries were written into the Combined Region and Segment ++Table Entry array and the Page Table Entry array in the Level-2 TLB ++. ++Counter:139 Name:TLB2_ENGINES_BUSY ++Description: ++The number of Level-2 TLB translation engines busy in a cycle ++. ++Counter:140 Name:TX_C_TEND ++Description: ++A TEND instruction has completed in a constrained transactional-execution ++mode ++. ++Counter:141 Name:TX_NC_TEND ++Description: ++A TEND instruction has completed in a non-constrained ++transactional-execution mode ++. ++Counter:143 Name:L1C_TLB2_MISSES ++Description: ++Increments by one for any cycle where a level-1 cache or level-2 TLB miss ++is in progress ++. ++Counter:144 Name:L1D_ONCHIP_L3_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from an On-Chip Level-3 cache without intervention ++. ++Counter:145 Name:L1D_ONCHIP_MEMORY_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from On-Chip memory ++. ++Counter:146 Name:L1D_ONCHIP_L3_SOURCED_WRITES_IV ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from an On-Chip Level-3 cache with intervention ++. ++Counter:147 Name:L1D_ONCLUSTER_L3_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from On-Cluster Level-3 cache withountervention ++. ++Counter:148 Name:L1D_ONCLUSTER_MEMORY_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from an On-Cluster memory ++. ++Counter:149 Name:L1D_ONCLUSTER_L3_SOURCED_WRITES_IV ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from an On-Cluster Level-3 cache with intervention ++. ++Counter:150 Name:L1D_OFFCLUSTER_L3_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from an Off-Cluster Level-3 cache without ++intervention ++. ++Counter:151 Name:L1D_OFFCLUSTER_MEMORY_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from Off-Cluster memory ++. ++Counter:152 Name:L1D_OFFCLUSTER_L3_SOURCED_WRITES_IV ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from an Off-Cluster Level-3 cache with intervention ++. ++Counter:153 Name:L1D_OFFDRAWER_L3_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from an Off-Drawer Level-3 cache without ++intervention ++. ++Counter:154 Name:L1D_OFFDRAWER_MEMORY_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from Off-Drawer memory ++. ++Counter:155 Name:L1D_OFFDRAWER_L3_SOURCED_WRITES_IV ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from an Off-Drawer Level-3 cache with intervention ++. ++Counter:156 Name:L1D_ONDRAWER_L4_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from On-Drawer Level-4 cache ++. ++Counter:157 Name:L1D_OFFDRAWER_L4_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from Off-Drawer Level-4 cache ++. ++Counter:158 Name:L1D_ONCHIP_L3_SOURCED_WRITES_RO ++Description: ++A directory write to the Level-1 Data cache directory where the returned ++cache line was sourced from On-Chip L3 but a read-only invalidate was ++done to remove other copies of the cache line ++. ++Counter:162 Name:L1I_ONCHIP_L3_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache ine was sourced from an On-Chip Level-3 cache without ++intervention ++. ++Counter:163 Name:L1I_ONCHIP_MEMORY_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache ine was sourced from On-Chip memory ++. ++Counter:164 Name:L1I_ONCHIP_L3_SOURCED_WRITES_IV ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache ine was sourced from an On-Chip Level-3 cache with ++intervention ++. ++Counter:165 Name:L1I_ONCLUSTER_L3_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache line was sourced from an On-Cluster Level-3 cache without ++intervention ++. ++Counter:166 Name:L1I_ONCLUSTER_MEMORY_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache line was sourced from an On-Cluster memory ++. ++Counter:167 Name:L1I_ONCLUSTER_L3_SOURCED_WRITES_IV ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache line was sourced from On-Cluster Level-3 cache with ++intervention ++. ++Counter:168 Name:L1I_OFFCLUSTER_L3_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache line was sourced from an Off-Cluster Level-3 cache without ++intervention ++. ++Counter:169 Name:L1I_OFFCLUSTER_MEMORY_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache line was sourced from Off-Cluster memory ++. ++Counter:170 Name:L1I_OFFCLUSTER_L3_SOURCED_WRITES_IV ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache line was sourced from an Off-Cluster Level-3 cache with ++intervention ++. ++Counter:171 Name:L1I_OFFDRAWER_L3_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache line was sourced from an Off-Drawer Level-3 cache without ++intervention ++. ++Counter:172 Name:L1I_OFFDRAWER_MEMORY_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache line was sourced from Off-Drawer memory ++. ++Counter:173 Name:L1I_OFFDRAWER_L3_SOURCED_WRITES_IV ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache line was sourced from an Off-Drawer Level-3 cache with ++intervention ++. ++Counter:174 Name:L1I_ONDRAWER_L4_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache line was sourced from On-Drawer Level-4 cache ++. ++Counter:175 Name:L1I_OFFDRAWER_L4_SOURCED_WRITES ++Description: ++A directory write to the Level-1 Instruction cache directory where the ++returned cache line was sourced from Off-Drawer Level-4 cache ++. ++Counter:224 Name:BCD_DFP_EXECUTION_SLOTS ++Description: ++Count of floating point execution slots used for finished Binary Coded ++Decimal to Decimal Floating Point conversions. Instructions: CDZT, ++CXZT, CZDT, CZXT ++. ++Counter:225 Name:VX_BCD_EXECUTION_SLOTS ++Description: ++Count of floating point execution slots used for finished vector arithmetic ++Binary Coded Decimal instructions. Instructions: VAP, VSP, VMPVMSP, VDP, ++VSDP, VRP, VLIP, VSRP, VPSOPVCP, VTP, VPKZ, VUPKZ, VCVB, VCVBG, VCVDVCVDG ++. ++Counter:226 Name:DECIMAL_INSTRUCTIONS ++Description: ++Decimal instructions dispatched. Instructions: CVB, CVD, AP, CP, DP, ED, ++EDMK, MP, SRP, SP, ZAP ++. ++Counter:233 Name:LAST_HOST_TRANSLATIONS ++Description: ++Last Host Translation done ++. ++Counter:243 Name:TX_NC_TABORT ++Description: ++A transaction abort has occurred in a non-constrained ++transactional-execution mode ++. ++Counter:244 Name:TX_C_TABORT_NO_SPECIAL ++Description: ++A transaction abort has occurred in a constrained transactional-execution ++mode and the CPU is not using any special logic to allow the transaction ++to complete ++. ++Counter:245 Name:TX_C_TABORT_SPECIAL ++Description: ++A transaction abort has occurred in a constrained transactional-execution ++mode and the CPU is using special logic to allow the transaction to ++complete ++. ++# ++# MT-diagnostic counter set ++# --------------------------------------------------------------------- ++Counter:448 Name:MT_DIAG_CYCLES_ONE_THR_ACTIVE ++Description: ++Cycle count with one thread active ++. ++Counter:449 Name:MT_DIAG_CYCLES_TWO_THR_ACTIVE ++Description: ++Cycle count with two threads active ++. +--- a/cpumf/data/cpum-cf-hw-counter.map ++++ b/cpumf/data/cpum-cf-hw-counter.map +@@ -14,4 +14,5 @@ + 2828 => 'cpum-cf-extended-zEC12.ctr', + 2964 => 'cpum-cf-extended-z13.ctr', + 2965 => 'cpum-cf-extended-z13.ctr', ++ 3906 => 'cpum-cf-extended-z14.ctr', + }; diff --git a/s390-tools-sles15sp1-01-lszcrypt-CEX6S-exploitation.patch b/s390-tools-sles15sp1-01-lszcrypt-CEX6S-exploitation.patch new file mode 100644 index 0000000..f2f281c --- /dev/null +++ b/s390-tools-sles15sp1-01-lszcrypt-CEX6S-exploitation.patch @@ -0,0 +1,124 @@ +Subject: lszcrypt: CEX6S exploitation +From: Harald Freudenberger + +Summary: s390-tools: Exploitation Support for CEX6S +Description: Exploitation Support for CEX6S +Upstream-ID: 31866fbfa4bd89606af2a313427ca06d230e20dc +Problem-ID: SEC1519 + +Upstream-Description: + + lszcrypt: CEX6S exploitation + + With z14 there comes a new crypto card 'CEX6S'. + + This patch introduces the s390-tools changes needed + to list the new card and show the capabilities correctly. + + Signed-off-by: Harald Freudenberger + Signed-off-by: Michael Holzheu + + +Signed-off-by: Harald Freudenberger +--- + zconf/zcrypt/lszcrypt.8 | 6 ++++++ + zconf/zcrypt/lszcrypt.c | 37 ++++++++++++++++++++++++++++--------- + 2 files changed, 34 insertions(+), 9 deletions(-) + +--- a/zconf/zcrypt/lszcrypt.8 ++++ b/zconf/zcrypt/lszcrypt.8 +@@ -85,6 +85,12 @@ EP11 Secure Key + .IP "o" + Long RNG + .RE ++ ++.RS 8 ++The CCA Secure Key capability may be limited by a hypervisor ++layer. The remarks 'full function set' or 'restricted function set' may ++reflect this. For details about these limitations please check the ++hypervisor documentation. + .TP 8 + .B -d, --domains + Shows the usage and control domains of the cryptographic devices. +--- a/zconf/zcrypt/lszcrypt.c ++++ b/zconf/zcrypt/lszcrypt.c +@@ -42,11 +42,19 @@ struct lszcrypt_l *lszcrypt_l = &l; + /* + * Card types + */ +-#define MASK_APSC 0x80000000 +-#define MASK_RSA4K 0x60000000 +-#define MASK_COPRO 0x10000000 +-#define MASK_ACCEL 0x08000000 +-#define MASK_EP11 0x04000000 ++#define MASK_APSC 0x80000000 ++#define MASK_RSA4K 0x60000000 ++#define MASK_COPRO 0x10000000 ++#define MASK_ACCEL 0x08000000 ++#define MASK_EP11 0x04000000 ++ ++/* ++ * Classification ++ */ ++#define MASK_CLASS_FULL 0x00800000 ++#define CLASS_FULL "full function set" ++#define MASK_CLASS_STATELESS 0x00400000 ++#define CLASS_STATELESS "restricted function set" + + /* + * Program configuration +@@ -226,7 +234,7 @@ static void show_capability(const char * + { + unsigned long func_val; + long hwtype, id; +- char *p, *ap, *dev, card[16]; ++ char *p, *ap, *dev, card[16], cbuf[256]; + + /* check if ap driver is available */ + ap = util_path_sysfs("bus/ap"); +@@ -250,6 +258,11 @@ static void show_capability(const char * + printf("Detailed capability information for %s (hardware type %ld) is not available.\n", card, hwtype); + return; + } ++ cbuf[0] = '\0'; ++ if (func_val & MASK_CLASS_FULL) ++ snprintf(cbuf, sizeof(cbuf), "%s", CLASS_FULL); ++ else if (func_val & MASK_CLASS_STATELESS) ++ snprintf(cbuf, sizeof(cbuf), "%s", CLASS_STATELESS); + printf("%s provides capability for:\n", card); + switch (hwtype) { + case 6: +@@ -262,11 +275,15 @@ static void show_capability(const char * + case 7: + case 9: + printf("%s\n", CAP_RSA4K); +- printf("%s\n", CAP_CCA); ++ if (cbuf[0]) ++ printf("%s (%s)\n", CAP_CCA, cbuf); ++ else ++ printf("%s\n", CAP_CCA); + printf("%s", CAP_RNG); + break; + case 10: + case 11: ++ case 12: + if (func_val & MASK_ACCEL) { + if (func_val & MASK_RSA4K) + printf("%s", CAP_RSA4K); +@@ -274,12 +291,14 @@ static void show_capability(const char * + printf("%s", CAP_RSA2K); + } else if (func_val & MASK_COPRO) { + printf("%s\n", CAP_RSA4K); +- printf("%s\n", CAP_CCA); ++ if (cbuf[0]) ++ printf("%s (%s)\n", CAP_CCA, cbuf); ++ else ++ printf("%s\n", CAP_CCA); + printf("%s", CAP_RNG); + } else if (func_val & MASK_EP11) { + printf("%s", CAP_EP11); + } else { +- + printf("Detailed capability information for %s (hardware type %ld) is not available.", card, hwtype); + } + break; diff --git a/s390-tools-sles15sp1-01-util_path-add-function-to-check-if-a-path-exists.patch b/s390-tools-sles15sp1-01-util_path-add-function-to-check-if-a-path-exists.patch new file mode 100644 index 0000000..74785a4 --- /dev/null +++ b/s390-tools-sles15sp1-01-util_path-add-function-to-check-if-a-path-exists.patch @@ -0,0 +1,55 @@ +Subject: util_path: add function to check if a path exists +From: Jan Hoeppner + +Summary: zpcictl: Add tool to manage PCI devices +Description: Use the zpcictl tool to manage PCI devices on the IBM Z + platform. Initial functions include generating firmware + error logs, resetting PCI devices, and preparing a device + for further repair actions. +Upstream-ID: df133846b5889a7698ac09f00284c1be54926b59 +Problem-ID: RAS1703 + +Upstream-Description: + + util_path: add function to check if a path exists + + GitHub-ID: #20 + + Signed-off-by: Rafael Fonseca + Acked-by: Michael Holzheu + Signed-off-by: Michael Holzheu + + +Signed-off-by: Jan Hoeppner +--- + include/lib/util_path.h | 1 + + libutil/util_path.c | 12 ++++++++++++ + 2 files changed, 13 insertions(+) + +--- a/include/lib/util_path.h ++++ b/include/lib/util_path.h +@@ -20,5 +20,6 @@ bool util_path_is_readable(const char *f + bool util_path_is_writable(const char *fmt, ...); + bool util_path_is_dir(const char *fmt, ...); + bool util_path_is_reg_file(const char *fmt, ...); ++bool util_path_exists(const char *fmt, ...); + + #endif /** LIB_UTIL_PATH_H @} */ +--- a/libutil/util_path.c ++++ b/libutil/util_path.c +@@ -194,3 +194,15 @@ free_str: + free(path); + return rc; + } ++ ++bool util_path_exists(const char *fmt, ...) ++{ ++ va_list ap; ++ char *path; ++ bool rc; ++ ++ UTIL_VASPRINTF(&path, fmt, ap); ++ rc = access(path, F_OK) == 0; ++ free(path); ++ return rc; ++} diff --git a/s390-tools-sles15sp1-02-cpumf-z14-split-counter-sets-according-to-CFVN-CSVN-.patch b/s390-tools-sles15sp1-02-cpumf-z14-split-counter-sets-according-to-CFVN-CSVN-.patch new file mode 100644 index 0000000..bffb743 --- /dev/null +++ b/s390-tools-sles15sp1-02-cpumf-z14-split-counter-sets-according-to-CFVN-CSVN-.patch @@ -0,0 +1,382 @@ +Subject: cpumf/z14: split counter sets according to CFVN/CSVN (part 1/2) +From: Hendrik Brueckner + +Summary: cpumf: Add CPU-MF hardware counters for z14 +Description: Add hardware counter definitions for IBM z14. +Upstream-ID: d121ffa3f01e08d2cc53140444dfcab830319012 +Problem-ID: KRN1608 + +Upstream-Description: + + cpumf/z14: split counter sets according to CFVN/CSVN (part 1/2) + + With z14, the counters in the problem-state are reduced resulting + in an increased first version number of the CPUM CF. To adapt to + this change, split the counter sets according to their counter + first and second version number. The second version number controls + the crypto-activity and extended counter set. Treat the crypto-activity + counter set as generic, as the extended counter set is already handled + based on hardware models. + + Signed-off-by: Hendrik Brueckner + Signed-off-by: Stefan Haberland + + +Signed-off-by: Hendrik Brueckner +--- + cpumf/Makefile | 4 - + cpumf/data/cpum-cf-cfvn-1.ctr | 48 +++++++++++++ + cpumf/data/cpum-cf-cfvn-3.ctr | 32 ++++++++ + cpumf/data/cpum-cf-csvn-generic.ctr | 84 ++++++++++++++++++++++ + cpumf/data/cpum-cf-generic.ctr | 132 ------------------------------------ + cpumf/data/cpum-cf-hw-counter.map | 15 +++- + 6 files changed, 180 insertions(+), 135 deletions(-) + +--- a/cpumf/Makefile ++++ b/cpumf/Makefile +@@ -4,7 +4,9 @@ include ../common.mak + + + CPUMF_DATADIR = $(TOOLS_DATADIR)/cpumf +-DATA_FILES = cpum-cf-hw-counter.map cpum-cf-generic.ctr \ ++DATA_FILES = cpum-cf-hw-counter.map \ ++ cpum-cf-cfvn-1.ctr cpum-cf-cfvn-3.ctr \ ++ cpum-cf-csvn-generic.ctr \ + cpum-cf-extended-z10.ctr cpum-cf-extended-z196.ctr \ + cpum-cf-extended-zEC12.ctr cpum-sf-modes.ctr \ + cpum-cf-extended-z13.ctr cpum-cf-extended-z14.ctr +--- /dev/null ++++ b/cpumf/data/cpum-cf-cfvn-1.ctr +@@ -0,0 +1,48 @@ ++Counter: 0 Name:CPU_CYCLES ++Description: ++Cycle Count ++. ++Counter: 1 Name:INSTRUCTIONS ++Description: ++Instruction Count ++. ++Counter: 2 Name:L1I_DIR_WRITES ++Description: ++Level-1 I-Cache Directory Write Count ++. ++Counter: 3 Name:L1I_PENALTY_CYCLES ++Description: ++Level-1 I-Cache Penalty Cycle Count ++. ++Counter: 4 Name:L1D_DIR_WRITES ++Description: ++Level-1 D-Cache Directory Write Count ++. ++Counter: 5 Name:L1D_PENALTY_CYCLES ++Description: ++Level-1 D-Cache Penalty Cycle Count ++. ++Counter: 32 Name:PROBLEM_STATE_CPU_CYCLES ++Description: ++Problem-State Cycle Count ++. ++Counter: 33 Name:PROBLEM_STATE_INSTRUCTIONS ++Description: ++Problem-State Instruction Count ++. ++Counter: 34 Name:PROBLEM_STATE_L1I_DIR_WRITES ++Description: ++Problem-State Level-1 I-Cache Directory Write Count ++. ++Counter: 35 Name:PROBLEM_STATE_L1I_PENALTY_CYCLES ++Description: ++Problem-State Level-1 I-Cache Penalty Cycle Count ++. ++Counter: 36 Name:PROBLEM_STATE_L1D_DIR_WRITES ++Description: ++Problem-State Level-1 D-Cache Directory Write Count ++. ++Counter: 37 Name:PROBLEM_STATE_L1D_PENALTY_CYCLES ++Description: ++Problem-State Level-1 D-Cache Penalty Cycle Count ++. +--- /dev/null ++++ b/cpumf/data/cpum-cf-cfvn-3.ctr +@@ -0,0 +1,32 @@ ++Counter: 0 Name:CPU_CYCLES ++Description: ++Cycle Count ++. ++Counter: 1 Name:INSTRUCTIONS ++Description: ++Instruction Count ++. ++Counter: 2 Name:L1I_DIR_WRITES ++Description: ++Level-1 I-Cache Directory Write Count ++. ++Counter: 3 Name:L1I_PENALTY_CYCLES ++Description: ++Level-1 I-Cache Penalty Cycle Count ++. ++Counter: 4 Name:L1D_DIR_WRITES ++Description: ++Level-1 D-Cache Directory Write Count ++. ++Counter: 5 Name:L1D_PENALTY_CYCLES ++Description: ++Level-1 D-Cache Penalty Cycle Count ++. ++Counter: 32 Name:PROBLEM_STATE_CPU_CYCLES ++Description: ++Problem-State Cycle Count ++. ++Counter: 33 Name:PROBLEM_STATE_INSTRUCTIONS ++Description: ++Problem-State Instruction Count ++. +--- /dev/null ++++ b/cpumf/data/cpum-cf-csvn-generic.ctr +@@ -0,0 +1,84 @@ ++Counter: 64 Name:PRNG_FUNCTIONS ++Description: ++Total number of the PRNG functions issued by the CPU ++. ++Counter: 65 Name:PRNG_CYCLES ++Description: ++Total number of CPU cycles when the DEA/AES coprocessor is busy ++performing PRNG functions issued by the CPU ++. ++Counter: 66 Name:PRNG_BLOCKED_FUNCTIONS ++Description: ++Total number of the PRNG functions that are issued by the CPU and are ++blocked because the DEA/AES coprocessor is busy performing a function ++issued by another CPU ++. ++Counter: 67 Name:PRNG_BLOCKED_CYCLES ++Description: ++Total number of CPU cycles blocked for the PRNG functions issued by ++the CPU because the DEA/AES coprocessor is busy performing a function ++issued by another CPU ++. ++Counter: 68 Name:SHA_FUNCTIONS ++Description: ++Total number of SHA functions issued by the CPU ++. ++Counter: 69 Name:SHA_CYCLES ++Description: ++Total number of CPU cycles when the SHA coprocessor is busy performing ++the SHA functions issued by the CPU ++. ++Counter: 70 Name:SHA_BLOCKED_FUNCTIONS ++Description: ++Total number of the SHA functions that are issued by the CPU and are ++blocked because the SHA coprocessor is busy performing a function issued ++by another CPU ++. ++Counter: 71 Name:SHA_BLOCKED_CYCLES ++Description: ++Total number of CPU cycles blocked for the SHA functions issued by the ++CPU because the SHA coprocessor is busy performing a function issued ++by another CPU ++. ++Counter: 72 Name:DEA_FUNCTIONS ++Description: ++Total number of the DEA functions issued by the CPU ++. ++Counter: 73 Name:DEA_CYCLES ++Description: ++Total number of CPU cycles when the DEA/AES coprocessor is busy ++performing the DEA functions issued by the CPU ++. ++Counter: 74 Name:DEA_BLOCKED_FUNCTIONS ++Description: ++Total number of the DEA functions that are issued by the CPU and are ++blocked because the DEA/AES coprocessor is busy performing a function ++issued by another CPU ++. ++Counter: 75 Name:DEA_BLOCKED_CYCLES ++Description: ++Total number of CPU cycles blocked for the DEA functions issued by the ++CPU because the DEA/AES coprocessor is busy performing a function issued ++by another CPU ++. ++Counter: 76 Name:AES_FUNCTIONS ++Description: ++Total number of AES functions issued by the CPU ++. ++Counter: 77 Name:AES_CYCLES ++Description: ++Total number of CPU cycles when the DEA/AES coprocessor is busy ++performing the AES functions issued by the CPU ++. ++Counter: 78 Name:AES_BLOCKED_FUNCTIONS ++Description: ++Total number of AES functions that are issued by the CPU and are blocked ++because the DEA/AES coprocessor is busy performing a function issued ++by another CPU ++. ++Counter: 79 Name:AES_BLOCKED_CYCLES ++Description: ++Total number of CPU cycles blocked for the AES functions issued by the ++CPU because the DEA/AES coprocessor is busy performing a function issued ++by another CPU ++. +--- a/cpumf/data/cpum-cf-generic.ctr ++++ /dev/null +@@ -1,132 +0,0 @@ +-Counter: 0 Name:CPU_CYCLES +-Description: +-Cycle Count +-. +-Counter: 1 Name:INSTRUCTIONS +-Description: +-Instruction Count +-. +-Counter: 2 Name:L1I_DIR_WRITES +-Description: +-Level-1 I-Cache Directory Write Count +-. +-Counter: 3 Name:L1I_PENALTY_CYCLES +-Description: +-Level-1 I-Cache Penalty Cycle Count +-. +-Counter: 4 Name:L1D_DIR_WRITES +-Description: +-Level-1 D-Cache Directory Write Count +-. +-Counter: 5 Name:L1D_PENALTY_CYCLES +-Description: +-Level-1 D-Cache Penalty Cycle Count +-. +-Counter: 32 Name:PROBLEM_STATE_CPU_CYCLES +-Description: +-Problem-State Cycle Count +-. +-Counter: 33 Name:PROBLEM_STATE_INSTRUCTIONS +-Description: +-Problem-State Instruction Count +-. +-Counter: 34 Name:PROBLEM_STATE_L1I_DIR_WRITES +-Description: +-Problem-State Level-1 I-Cache Directory Write Count +-. +-Counter: 35 Name:PROBLEM_STATE_L1I_PENALTY_CYCLES +-Description: +-Problem-State Level-1 I-Cache Penalty Cycle Count +-. +-Counter: 36 Name:PROBLEM_STATE_L1D_DIR_WRITES +-Description: +-Problem-State Level-1 D-Cache Directory Write Count +-. +-Counter: 37 Name:PROBLEM_STATE_L1D_PENALTY_CYCLES +-Description: +-Problem-State Level-1 D-Cache Penalty Cycle Count +-. +-Counter: 64 Name:PRNG_FUNCTIONS +-Description: +-Total number of the PRNG functions issued by the CPU +-. +-Counter: 65 Name:PRNG_CYCLES +-Description: +-Total number of CPU cycles when the DEA/AES coprocessor is busy +-performing PRNG functions issued by the CPU +-. +-Counter: 66 Name:PRNG_BLOCKED_FUNCTIONS +-Description: +-Total number of the PRNG functions that are issued by the CPU and are +-blocked because the DEA/AES coprocessor is busy performing a function +-issued by another CPU +-. +-Counter: 67 Name:PRNG_BLOCKED_CYCLES +-Description: +-Total number of CPU cycles blocked for the PRNG functions issued by +-the CPU because the DEA/AES coprocessor is busy performing a function +-issued by another CPU +-. +-Counter: 68 Name:SHA_FUNCTIONS +-Description: +-Total number of SHA functions issued by the CPU +-. +-Counter: 69 Name:SHA_CYCLES +-Description: +-Total number of CPU cycles when the SHA coprocessor is busy performing +-the SHA functions issued by the CPU +-. +-Counter: 70 Name:SHA_BLOCKED_FUNCTIONS +-Description: +-Total number of the SHA functions that are issued by the CPU and are +-blocked because the SHA coprocessor is busy performing a function issued +-by another CPU +-. +-Counter: 71 Name:SHA_BLOCKED_CYCLES +-Description: +-Total number of CPU cycles blocked for the SHA functions issued by the +-CPU because the SHA coprocessor is busy performing a function issued +-by another CPU +-. +-Counter: 72 Name:DEA_FUNCTIONS +-Description: +-Total number of the DEA functions issued by the CPU +-. +-Counter: 73 Name:DEA_CYCLES +-Description: +-Total number of CPU cycles when the DEA/AES coprocessor is busy +-performing the DEA functions issued by the CPU +-. +-Counter: 74 Name:DEA_BLOCKED_FUNCTIONS +-Description: +-Total number of the DEA functions that are issued by the CPU and are +-blocked because the DEA/AES coprocessor is busy performing a function +-issued by another CPU +-. +-Counter: 75 Name:DEA_BLOCKED_CYCLES +-Description: +-Total number of CPU cycles blocked for the DEA functions issued by the +-CPU because the DEA/AES coprocessor is busy performing a function issued +-by another CPU +-. +-Counter: 76 Name:AES_FUNCTIONS +-Description: +-Total number of AES functions issued by the CPU +-. +-Counter: 77 Name:AES_CYCLES +-Description: +-Total number of CPU cycles when the DEA/AES coprocessor is busy +-performing the AES functions issued by the CPU +-. +-Counter: 78 Name:AES_BLOCKED_FUNCTIONS +-Description: +-Total number of AES functions that are issued by the CPU and are blocked +-because the DEA/AES coprocessor is busy performing a function issued +-by another CPU +-. +-Counter: 79 Name:AES_BLOCKED_CYCLES +-Description: +-Total number of CPU cycles blocked for the AES functions issued by the +-CPU because the DEA/AES coprocessor is busy performing a function issued +-by another CPU +-. +--- a/cpumf/data/cpum-cf-hw-counter.map ++++ b/cpumf/data/cpum-cf-hw-counter.map +@@ -1,11 +1,22 @@ + # CPU-measurement facilities + # +-# Mapping of IBM System z hardware types to extended counter set defintions ++# Mapping of: ++# 1. CPU-MF counter first/second version numbers to "generic" counter ++# definitions ++# 2. IBM z Systems hardware to respective extended counter set definitions + # + # + { + # Definition # File name +- 0 => 'cpum-cf-generic.ctr', ++ ++ # CFVN ++ 'cfvn-1' => 'cpum-cf-cfvn-1.ctr', ++ 'cfvn-3' => 'cpum-cf-cfvn-3.ctr', ++ ++ # CSVN ++ 'csvn-generic' => 'cpum-cf-csvn-generic.ctr', ++ ++ # Extended counters + 2097 => 'cpum-cf-extended-z10.ctr', + 2098 => 'cpum-cf-extended-z10.ctr', + 2817 => 'cpum-cf-extended-z196.ctr', diff --git a/s390-tools-sles15sp1-02-lszcrypt-fix-date-and-wrong-indentation.patch b/s390-tools-sles15sp1-02-lszcrypt-fix-date-and-wrong-indentation.patch new file mode 100644 index 0000000..91c03c4 --- /dev/null +++ b/s390-tools-sles15sp1-02-lszcrypt-fix-date-and-wrong-indentation.patch @@ -0,0 +1,56 @@ +Subject: lszcrypt: fix date and wrong indentation +From: Harald Freudenberger + +Summary: s390-tools: Exploitation Support for CEX6S +Description: Exploitation Support for CEX6S +Upstream-ID: 4ad5e29f2f02e02c772ca4707b9f10253b1e5692 +Problem-ID: SEC1519 + +Upstream-Description: + + lszcrypt: fix date and wrong indentation + + The man page date was AUG 2008. Changed to OCT 2017. + A previous commit had a wrong indentation on following + options text for lszcrypt. Fixed. + + Signed-off-by: Harald Freudenberger + Signed-off-by: Stefan Haberland + + +Signed-off-by: Harald Freudenberger +--- + zconf/zcrypt/chzcrypt.8 | 2 +- + zconf/zcrypt/lszcrypt.8 | 3 ++- + 2 files changed, 3 insertions(+), 2 deletions(-) + +--- a/zconf/zcrypt/chzcrypt.8 ++++ b/zconf/zcrypt/chzcrypt.8 +@@ -2,7 +2,7 @@ + .\" s390-tools is free software; you can redistribute it and/or modify + .\" it under the terms of the MIT license. See LICENSE for details. + .\" +-.TH CHZCRYPT 8 "AUG 2008" "s390-tools" ++.TH CHZCRYPT 8 "OCT 2017" "s390-tools" + .SH NAME + chzcrypt \- modify zcrypt configuration + .SH SYNOPSIS +--- a/zconf/zcrypt/lszcrypt.8 ++++ b/zconf/zcrypt/lszcrypt.8 +@@ -10,7 +10,7 @@ + .\" nroff -man lszcrypt.8 + .\" to process this source + .\" +-.TH LSZCRYPT 8 "AUG 2008" "s390-tools" ++.TH LSZCRYPT 8 "OCT 2017" "s390-tools" + .SH NAME + lszcrypt \- display zcrypt device and configuration information + .SH SYNOPSIS +@@ -91,6 +91,7 @@ The CCA Secure Key capability may be lim + layer. The remarks 'full function set' or 'restricted function set' may + reflect this. For details about these limitations please check the + hypervisor documentation. ++.RE + .TP 8 + .B -d, --domains + Shows the usage and control domains of the cryptographic devices. diff --git a/s390-tools-sles15sp1-02-util_path-Add-description-for-util_path_exists.patch b/s390-tools-sles15sp1-02-util_path-Add-description-for-util_path_exists.patch new file mode 100644 index 0000000..72fb9e3 --- /dev/null +++ b/s390-tools-sles15sp1-02-util_path-Add-description-for-util_path_exists.patch @@ -0,0 +1,43 @@ +Subject: util_path: Add description for util_path_exists() +From: Jan Hoeppner + +Summary: zpcictl: Add tool to manage PCI devices +Description: Use the zpcictl tool to manage PCI devices on the IBM Z + platform. Initial functions include generating firmware + error logs, resetting PCI devices, and preparing a device + for further repair actions. +Upstream-ID: d0e2caf0ffb195568bba89a95549a5a4f026a4e6 +Problem-ID: RAS1703 + +Upstream-Description: + + util_path: Add description for util_path_exists() + + Signed-off-by: Michael Holzheu + + +Signed-off-by: Jan Hoeppner +--- + libutil/util_path.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +--- a/libutil/util_path.c ++++ b/libutil/util_path.c +@@ -195,6 +195,17 @@ free_str: + return rc; + } + ++/** ++ * Test if path to directory or file exists ++ * ++ * This function has the same semantics as "-e path" in bash. ++ * ++ * @param[in] fmt Format string for path to test ++ * @param[in] ... Variable arguments for format string ++ * ++ * @returns true Path exists ++ * false Otherwise ++ */ + bool util_path_exists(const char *fmt, ...) + { + va_list ap; diff --git a/s390-tools-sles15sp1-03-cpumf-cpumf_helper-read-split-counter-sets-part-2-2.patch b/s390-tools-sles15sp1-03-cpumf-cpumf_helper-read-split-counter-sets-part-2-2.patch new file mode 100644 index 0000000..b8e6dfe --- /dev/null +++ b/s390-tools-sles15sp1-03-cpumf-cpumf_helper-read-split-counter-sets-part-2-2.patch @@ -0,0 +1,107 @@ +Subject: cpumf/cpumf_helper: read split counter sets (part 2/2) +From: Hendrik Brueckner + +Summary: cpumf: Add CPU-MF hardware counters for z14 +Description: Add hardware counter definitions for IBM z14. +Upstream-ID: 1064e5b9cc3bdeb5731c2e152ce146dfdad27e6f +Problem-ID: KRN1608 + +Upstream-Description: + + cpumf/cpumf_helper: read split counter sets (part 2/2) + + Update the cpumf helper program to read the split counter set + definition files. Changes to higher-level program like lscpumf + are not necessary. + + Signed-off-by: Hendrik Brueckner + Signed-off-by: Stefan Haberland + + +Signed-off-by: Hendrik Brueckner +--- + cpumf/bin/cpumf_helper.in | 50 ++++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 42 insertions(+), 8 deletions(-) + +--- a/cpumf/bin/cpumf_helper.in ++++ b/cpumf/bin/cpumf_helper.in +@@ -229,6 +229,28 @@ sub get_hardware_type() + return $type; + } + ++sub get_cpum_cf_version() ++{ ++ my $SL; ++ ++ my $v = { ++ cfvn => 0, ++ csvn => 0, ++ }; ++ ++ return $v unless open($SL, '<', $SERVICE_LEVELS); ++ while (my $line = <$SL>) { ++ # CPU-MF: Counter facility: version=3.5 ++ if ($line =~ m/^CPU-MF: Counter facility: version=(\d+)\.(\d+)/) { ++ $v->{cfvn} = $1; # Counter First Version Number ++ $v->{csvn} = $2; # Counter Second Version Number ++ last; ++ } ++ } ++ close($SL); ++ return $v ++} ++ + sub cpumf_load_ctrdef($;$) + { + my $hw_type = shift(); +@@ -237,10 +259,20 @@ sub cpumf_load_ctrdef($;$) + my $ctrmap = cpumf_hardware_counter_map(); + return unless $ctrmap; + ++ # Obtain CPU-MF counter facility versions ++ my $version = get_cpum_cf_version(); ++ ++ # List of "generic" counter sets ++ my @def = (); ++ push @def, "cfvn-" . $version->{cfvn}; ++ push @def, "csvn-generic"; ++ + my $h = {}; +- # Load generic counter sets +- cpumf_parse_ctrdef($ctrmap->{0}, $h) or +- croak "Failed to read generic counter definition: $!\n"; ++ # Load counter set definition ++ foreach my $ent (@def) { ++ cpumf_parse_ctrdef($ctrmap->{$ent}, $h) or ++ croak "Failed to read counter definition for $ent: $!\n"; ++ } + # Load hardware model specific counter set(s) + if ($hw_type && $ctrmap->{$hw_type}) { + # Hardware-model specific counter sets are: +@@ -323,7 +355,7 @@ sub cpumf_helper_main() + GetOptions( + "i|info" => \$conf->{opt_info}, + "c|counter=i" => \$conf->{opt_ctr}, +- "ctr-def=i" => \$conf->{opt_ctrdef}, ++ "ctr-def=s" => \$conf->{opt_ctrdef}, + "hardware-type" => \$conf->{opt_hwtype}, + "ctr-set-names" => \$conf->{opt_ctrset_names}, + "ctr-set-ids" => \$conf->{opt_ctrset_ids}, +@@ -428,11 +460,13 @@ B<--ctr-def> option and specify the Syst + + Displays the System z hardware type. + +-=item B<--ctr-def> I ++=item B<--ctr-def> I + +-Displays detailed information about a particular counter set for the specified +-System z hardware type, I. If you specify zero for +-I, type-independent counter sets are displayed. ++Displays detailed information about the specified counter definition. ++Valid counter definitions start with C or followed by ++the counter first/second version number of the CPU-Measurement Counter ++Facility. To display counter information of model-specific counter ++sets, specify the System z hardware type for I. + + =item B<--ctr-set-names> + diff --git a/s390-tools-sles15sp1-03-util_path-Make-true-false-handling-consistent-with-o.patch b/s390-tools-sles15sp1-03-util_path-Make-true-false-handling-consistent-with-o.patch new file mode 100644 index 0000000..0ac8c00 --- /dev/null +++ b/s390-tools-sles15sp1-03-util_path-Make-true-false-handling-consistent-with-o.patch @@ -0,0 +1,34 @@ +Subject: util_path: Make true/false handling consistent with other functions +From: Jan Hoeppner + +Summary: zpcictl: Add tool to manage PCI devices +Description: Use the zpcictl tool to manage PCI devices on the IBM Z + platform. Initial functions include generating firmware + error logs, resetting PCI devices, and preparing a device + for further repair actions. +Upstream-ID: 2b92bc4c087fd7a2275ba8fd5608cf3c86cdcc98 +Problem-ID: RAS1703 + +Upstream-Description: + + util_path: Make true/false handling consistent with other functions + + Signed-off-by: Michael Holzheu + + +Signed-off-by: Jan Hoeppner +--- + libutil/util_path.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/libutil/util_path.c ++++ b/libutil/util_path.c +@@ -213,7 +213,7 @@ bool util_path_exists(const char *fmt, . + bool rc; + + UTIL_VASPRINTF(&path, fmt, ap); +- rc = access(path, F_OK) == 0; ++ rc = access(path, F_OK) == 0 ? true : false; + free(path); + return rc; + } diff --git a/s390-tools-sles15sp1-04-cpumf-correct-z14-counter-number.patch b/s390-tools-sles15sp1-04-cpumf-correct-z14-counter-number.patch new file mode 100644 index 0000000..45e1127 --- /dev/null +++ b/s390-tools-sles15sp1-04-cpumf-correct-z14-counter-number.patch @@ -0,0 +1,32 @@ +Subject: cpumf: correct z14 counter number +From: Hendrik Brueckner + +Summary: cpumf: Add CPU-MF hardware counters for z14 +Description: Add hardware counter definitions for IBM z14. +Upstream-ID: 144bddbf5bce749549a289acbeb49337edaaea45 +Problem-ID: KRN1608 + +Upstream-Description: + + cpumf: correct z14 counter number + + Signed-off-by: Hendrik Brueckner + Signed-off-by: Stefan Haberland + + +Signed-off-by: Hendrik Brueckner +--- + cpumf/data/cpum-cf-extended-z14.ctr | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/cpumf/data/cpum-cf-extended-z14.ctr ++++ b/cpumf/data/cpum-cf-extended-z14.ctr +@@ -269,7 +269,7 @@ Description: + Decimal instructions dispatched. Instructions: CVB, CVD, AP, CP, DP, ED, + EDMK, MP, SRP, SP, ZAP + . +-Counter:233 Name:LAST_HOST_TRANSLATIONS ++Counter:232 Name:LAST_HOST_TRANSLATIONS + Description: + Last Host Translation done + . diff --git a/s390-tools-sles15sp1-04-zpcictl-Introduce-new-tool-zpcictl.patch b/s390-tools-sles15sp1-04-zpcictl-Introduce-new-tool-zpcictl.patch new file mode 100644 index 0000000..880ce29 --- /dev/null +++ b/s390-tools-sles15sp1-04-zpcictl-Introduce-new-tool-zpcictl.patch @@ -0,0 +1,603 @@ +Subject: zpcictl: Introduce new tool zpcictl +From: Jan Hoeppner + +Summary: zpcictl: Add tool to manage PCI devices +Description: Use the zpcictl tool to manage PCI devices on the IBM Z + platform. Initial functions include generating firmware + error logs, resetting PCI devices, and preparing a device + for further repair actions. +Upstream-ID: 177cf8cfeb83f85bc164c462b5534f93be3bd979 +Problem-ID: RAS1703 + +Upstream-Description: + + zpcictl: Introduce new tool zpcictl + + zpcictl is used to manage PCI devices on z Systems. In this first + version it is mainly used to handle erroneous PCI devices by changing + their state and make those changes known to the SE. Log data, such as + S.M.A.R.T. data for NVMe devices, is sent alongside those state changes. + + The state change is issued by sending data via the PCI 'report_error' + sysfs attribute. It's a binary attribute which will cause the host to + send an Adapter Notification Event. + + Signed-off-by: Jan Höppner + + +Signed-off-by: Jan Hoeppner +--- + .gitignore | 1 + Makefile | 2 + zpcictl/Makefile | 18 ++ + zpcictl/zpcictl.8 | 80 +++++++++++ + zpcictl/zpcictl.c | 378 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + zpcictl/zpcictl.h | 60 ++++++++ + 6 files changed, 538 insertions(+), 1 deletion(-) + +--- a/.gitignore ++++ b/.gitignore +@@ -87,3 +87,4 @@ zipl/boot/data.h + zipl/src/chreipl_helper.device-mapper + zipl/src/zipl + zkey/zkey ++zpcictl/zpcictl +--- a/Makefile ++++ b/Makefile +@@ -8,7 +8,7 @@ TOOL_DIRS = zipl zdump fdasd dasdfmt das + tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \ + vmconvert vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \ + ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \ +- systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot ++ systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot zpcictl + SUB_DIRS = $(LIB_DIRS) $(TOOL_DIRS) + + all: $(TOOL_DIRS) +--- /dev/null ++++ b/zpcictl/Makefile +@@ -0,0 +1,18 @@ ++include ../common.mak ++ ++all: zpcictl ++ ++libs = $(rootdir)/libutil/libutil.a ++ ++zpcictl: zpcictl.o $(libs) ++ ++install: all ++ $(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man8 ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zpcictl $(DESTDIR)$(BINDIR) ++ $(INSTALL) -g $(GROUP) -o $(OWNER) -m 644 zpcictl.8 \ ++ $(DESTDIR)$(MANDIR)/man8 ++ ++clean: ++ rm -f *.o *~ zpcictl core ++ ++.PHONY: all install clean +--- /dev/null ++++ b/zpcictl/zpcictl.8 +@@ -0,0 +1,80 @@ ++.\" Copyright 2017 IBM Corp. ++.\" s390-tools is free software; you can redistribute it and/or modify ++.\" it under the terms of the MIT license. See LICENSE for details. ++.\" ++.\" Macro for inserting an option description prologue. ++.\" .OD [] [args] ++.de OD ++. ds args " ++. if !'\\$3'' .as args \fI\\$3\fP ++. if !'\\$4'' .as args \\$4 ++. if !'\\$5'' .as args \fI\\$5\fP ++. if !'\\$6'' .as args \\$6 ++. if !'\\$7'' .as args \fI\\$7\fP ++. PD 0 ++. if !'\\$2'' .IP "\fB\-\\$2\fP \\*[args]" 4 ++. if !'\\$1'' .IP "\fB\-\-\\$1\fP \\*[args]" 4 ++. PD ++.. ++. ++.TH zpcictl 8 "Oct 2018" s390-tools zpcictl ++. ++.SH NAME ++zpcictl - Manage PCI devices on z Systems ++. ++. ++.SH SYNOPSIS ++.B "zpcictl" ++.I "OPTIONS" ++.I "DEVICE" ++. ++. ++.SH DESCRIPTION ++.B zpcictl ++is a tool for managing PCI devices on the IBM z Systems platform. It is ++especially used for reporting errorneous PCI devices to the service element. ++ ++.B Note: ++For NVMe devices additional data (such as S.M.A.R.T. data) is collected and sent ++with any error handling action. The smartmontools are required to be installed ++for this to work. ++.PP ++. ++. ++.SH DEVICE ++.B DEVICE ++can be either the PCI slot address (e.g. 0000:00:00.0) or the main device node ++of an NVMe device (e.g. /dev/nvme0). ++. ++. ++.SH OPTIONS ++.SS Error Handling ++.OD reset "" "DEVICE" ++Reset ++.I DEVICE ++and initiate a re-initialisation of the adapter. ++.PP ++. ++.OD deconfigure "" "DEVICE" ++De-configure ++.I DEVICE ++and prepare for any repair action. This action will move the ++PCI device from a configured to a reserved state. ++.PP ++. ++.OD report-error "" "DEVICE" ++Report any device error for ++.IR DEVICE . ++The ++.I DEVICE ++is marked as erroneous and no further action is initiated on it. ++.PP ++. ++.SS Misc ++.OD help "h" "" ++Print usage information, then exit. ++.PP ++. ++.OD version "v" "" ++Print version information, then exit. ++.PP +--- /dev/null ++++ b/zpcictl/zpcictl.c +@@ -0,0 +1,378 @@ ++/* ++ * zpcictl - Manage PCI devices on z Systems ++ * ++ * Copyright IBM Corp. 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 "lib/util_base.h" ++#include "lib/util_libc.h" ++#include "lib/util_opt.h" ++#include "lib/util_path.h" ++#include "lib/util_prg.h" ++#include "lib/util_proc.h" ++#include "lib/util_rec.h" ++#include "lib/util_scandir.h" ++ ++#include "zpcictl.h" ++ ++#define SMARTCTL_CMDLINE "smartctl -x %s 2>/dev/null" ++ ++static const struct util_prg prg = { ++ .desc = "Use zpcictl to manage PCI devices on s390\n" ++ "DEVICE is the slot id or node of the device (e.g. /dev/nvme0)", ++ .args = "DEVICE", ++ .copyright_vec = { ++ { ++ .owner = "IBM Corp.", ++ .pub_first = 2018, ++ .pub_last = 2018, ++ }, ++ UTIL_PRG_COPYRIGHT_END ++ } ++}; ++ ++/* Defines for options with no short command */ ++#define OPT_RESET 128 ++#define OPT_DECONF 129 ++#define OPT_REPORT_ERR 130 ++ ++static struct util_opt opt_vec[] = { ++ UTIL_OPT_SECTION("ERROR HANDLING"), ++ { ++ .option = { "reset", no_argument, NULL, OPT_RESET }, ++ .desc = "Reset device", ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ { ++ .option = { "deconfigure", no_argument, NULL, OPT_DECONF }, ++ .desc = "De-configure device and prepare for any repair action", ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ { ++ .option = { "report-error", no_argument, NULL, OPT_REPORT_ERR }, ++ .desc = "Report device error to service element (SE)", ++ .flags = UTIL_OPT_FLAG_NOSHORT, ++ }, ++ UTIL_OPT_SECTION("MISC"), ++ UTIL_OPT_HELP, ++ UTIL_OPT_VERSION, ++ UTIL_OPT_END ++}; ++ ++static int is_char_dev(const char *dev) ++{ ++ struct stat s; ++ ++ if (stat(dev, &s)) ++ return 0; ++ ++ return S_ISCHR(s.st_mode); ++} ++ ++static int is_blk_dev(const char *dev) ++{ ++ struct stat s; ++ ++ if (stat(dev, &s)) ++ return 0; ++ ++ return S_ISBLK(s.st_mode); ++} ++ ++static void fopen_err(char *path) ++{ ++ warnx("Could not open file %s: %s", path, strerror(errno)); ++ free(path); ++ exit(EXIT_FAILURE); ++} ++ ++#define READ_CHUNK_SIZE 512 ++ ++static char *collect_smart_data(struct zpci_device *pdev) ++{ ++ char *buffer = NULL; ++ size_t count = 0; ++ char *cmd; ++ FILE *fd; ++ ++ util_asprintf(&cmd, SMARTCTL_CMDLINE, pdev->device); ++ fd = popen(cmd, "r"); ++ if (!fd) ++ goto out; ++ ++ while (!feof(fd)) { ++ buffer = realloc(buffer, count + READ_CHUNK_SIZE); ++ if (!buffer) { ++ warnx("Could not collect S.M.A.R.T. data"); ++ goto out; ++ } ++ count += fread(&buffer[count], 1, READ_CHUNK_SIZE, fd); ++ if (ferror(fd)) { ++ free(buffer); ++ buffer = NULL; ++ goto out; ++ } ++ } ++ ++ buffer = realloc(buffer, count); ++ if (!buffer && count > 0) ++ warnx("Could not collect S.M.A.R.T. data"); ++ if (buffer) ++ buffer[count] = '\0'; ++ ++out: ++ pclose(fd); ++ free(cmd); ++ ++ return buffer; ++} ++ ++static unsigned int sysfs_read_value(struct zpci_device *pdev, const char *attr) ++{ ++ unsigned int val; ++ char *path; ++ FILE *fp; ++ ++ path = util_path_sysfs("bus/pci/devices/%s/%s", pdev->slot, attr); ++ fp = fopen(path, "r"); ++ if (!fp) ++ fopen_err(path); ++ fscanf(fp, "%x", &val); ++ fclose(fp); ++ free(path); ++ ++ return val; ++} ++ ++static void sysfs_write_data(struct zpci_report_error *report, char *slot) ++{ ++ char *path; ++ int fd, rc; ++ ++ path = util_path_sysfs("bus/pci/devices/%s/report_error", slot); ++ fd = open(path, O_WRONLY); ++ if (!fd) ++ fopen_err(path); ++ rc = write(fd, report, sizeof(*report)); ++ if (rc == -1) ++ warnx("Could not write to file: %s: %s", path, strerror(errno)); ++ if (close(fd)) ++ warnx("Could not close file: %s: %s", path, strerror(errno)); ++ free(path); ++} ++ ++static void sysfs_get_slot_addr(const char *dev, char *slot) ++{ ++ unsigned int major, minor; ++ struct stat dev_stat; ++ char addr[13]; ++ char *path; ++ FILE *fp; ++ ++ if (stat(dev, &dev_stat) != 0) { ++ errx(EXIT_FAILURE, "Could not get stat information for %s: %s", ++ dev, strerror(errno)); ++ } ++ major = major(dev_stat.st_rdev); ++ minor = minor(dev_stat.st_rdev); ++ ++ path = util_path_sysfs("dev/char/%u:%u/address", major, minor); ++ fp = fopen(path, "r"); ++ if (!fp) ++ fopen_err(path); ++ fscanf(fp, "%s", addr); ++ fclose(fp); ++ free(path); ++ ++ strcpy(slot, addr); ++} ++ ++static void get_device_node(struct zpci_device *pdev) ++{ ++ struct dirent **de_vec; ++ char *path, *dev; ++ char slot[13]; ++ int count, i; ++ ++ path = util_path_sysfs("bus/pci/devices/%s/nvme", pdev->slot); ++ count = util_scandir(&de_vec, alphasort, path, "nvme*"); ++ if (count == -1) { ++ warnx("Could not read directory %s: %s", path, strerror(errno)); ++ free(path); ++ exit(EXIT_FAILURE); ++ } ++ ++ for (i = 0; i < count; i++) { ++ util_asprintf(&dev, "/dev/%s", de_vec[i]->d_name); ++ sysfs_get_slot_addr(dev, slot); ++ if (strcmp(slot, pdev->slot) == 0) { ++ pdev->device = dev; ++ break; ++ } ++ } ++ ++ util_scandir_free(de_vec, count); ++ free(path); ++} ++ ++static int device_exists(char *dev) ++{ ++ char *path; ++ int rc = 0; ++ ++ path = util_path_sysfs("bus/pci/devices/%s", dev); ++ if (util_path_exists(path) || util_path_exists(dev)) ++ rc = 1; ++ free(path); ++ ++ return rc; ++} ++ ++static void get_device_info(struct zpci_device *pdev, char *dev) ++{ ++ if (!device_exists(dev)) ++ errx(EXIT_FAILURE, "Device %s not found", dev); ++ if (is_blk_dev(dev)) ++ errx(EXIT_FAILURE, "Unsupported device type %s", dev); ++ if (is_char_dev(dev)) { ++ sysfs_get_slot_addr(dev, pdev->slot); ++ pdev->device = dev; ++ } else { ++ strcpy(pdev->slot, dev); ++ } ++ ++ pdev->class = sysfs_read_value(pdev, "class"); ++ pdev->fid = sysfs_read_value(pdev, "function_id"); ++ pdev->pchid = sysfs_read_value(pdev, "pchid"); ++ ++ /* In case a slot address was specified, we still need to figure out ++ * the device node for NVMe devices. Otherwise we won't be able to ++ * collect S.M.A.R.T. data at a later point. ++ */ ++ if (!pdev->device && pdev->class == PCI_CLASS_NVME) ++ get_device_node(pdev); ++} ++ ++/* ++ * Issue an SCLP Adapter Error Notification event with a specific action ++ * qualifier. ++ * ++ * Collect additional information when possible (e.g. S.M.A.R.T. data for NVMe ++ * devices). ++ */ ++static void sclp_issue_action(struct zpci_device *pdev, int action) ++{ ++ struct zpci_report_error report = { ++ .header = { 0 }, ++ .data = { 0 } ++ }; ++ char *sdata = NULL; ++ ++ report.header.version = 1; ++ report.header.action = action; ++ report.header.length = sizeof(report.data); ++ report.data.timestamp = (__u64)time(NULL); ++ report.data.err_log_id = 0x4713; ++ ++ if (pdev->class == PCI_CLASS_NVME) ++ sdata = collect_smart_data(pdev); ++ if (sdata) { ++ strncpy(report.data.log_data, sdata, sizeof(report.data.log_data)); ++ free(sdata); ++ } ++ sysfs_write_data(&report, pdev->slot); ++} ++ ++/* ++ * Reset the PCI device and initiate a re-initialization. ++ */ ++static void sclp_reset_device(struct zpci_device *pdev) ++{ ++ sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_RESET); ++} ++ ++/* ++ * De-Configure/repair PCI device. Moves the device from configured ++ * to reserved state. ++ */ ++static void sclp_deconfigure(struct zpci_device *pdev) ++{ ++ sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_DECONF); ++} ++ ++/* ++ * Report an error to the SE. ++ */ ++static void sclp_report_error(struct zpci_device *pdev) ++{ ++ sclp_issue_action(pdev, SCLP_ERRNOTIFY_AQ_REPORT_ERR); ++} ++ ++static void parse_cmdline(int argc, char *argv[], struct options *opts) ++{ ++ int cmd; ++ ++ util_prg_init(&prg); ++ util_opt_init(opt_vec, NULL); ++ ++ do { ++ cmd = util_opt_getopt_long(argc, argv); ++ ++ switch (cmd) { ++ case OPT_RESET: ++ opts->reset = 1; ++ break; ++ case OPT_DECONF: ++ opts->deconfigure = 1; ++ break; ++ case OPT_REPORT_ERR: ++ opts->report = 1; ++ break; ++ case 'h': ++ util_prg_print_help(); ++ util_opt_print_help(); ++ exit(EXIT_SUCCESS); ++ case 'v': ++ util_prg_print_version(); ++ exit(EXIT_SUCCESS); ++ case -1: ++ /* End of options string */ ++ if (argc == 1) { ++ errx(EXIT_FAILURE, ++ "Use '%s --help' for more information", ++ argv[0]); ++ } ++ break; ++ } ++ } while (cmd != -1); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ struct zpci_device pdev = { 0 }; ++ struct options opts = { 0 }; ++ ++ parse_cmdline(argc, argv, &opts); ++ ++ if (optind >= argc) ++ errx(EXIT_FAILURE, "No device specified"); ++ ++ get_device_info(&pdev, argv[optind]); ++ ++ if (opts.reset) ++ sclp_reset_device(&pdev); ++ else if (opts.deconfigure) ++ sclp_deconfigure(&pdev); ++ else if (opts.report) ++ sclp_report_error(&pdev); ++ ++ return 0; ++} +--- /dev/null ++++ b/zpcictl/zpcictl.h +@@ -0,0 +1,60 @@ ++/* ++ * zpcictl - Manage PCI devices on z Systems ++ * ++ * Copyright IBM Corp. 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. ++ */ ++ ++#ifndef ZPCICTL_H ++#define ZPCICTL_H ++ ++#include ++#include "lib/zt_common.h" ++ ++#define SCLP_ERRNOTIFY_AQ_RESET 0 ++#define SCLP_ERRNOTIFY_AQ_DECONF 1 ++#define SCLP_ERRNOTIFY_AQ_REPORT_ERR 2 ++ ++#define PCI_CLASS_UNCLASSIFIED 0x000000U ++#define PCI_CLASS_NVME 0x010802U ++#define PCI_CLASS_NETWORK 0x020000U ++ ++struct options { ++ unsigned int reset; ++ unsigned int deconfigure; ++ unsigned int report; ++}; ++ ++struct zpci_device { ++ u16 fid; ++ u16 pchid; ++ u32 class; ++ char slot[13]; ++ char *device; ++}; ++ ++struct zpci_report_error_header { ++ __u8 version; /* Interface version byte */ ++ __u8 action; /* Action qualifier byte ++ * 0: Adapter Reset Request ++ * 1: Deconfigure and repair action requested ++ * 2: Informational Report ++ */ ++ __u16 length; /* Length of Subsequent Data (up to 4K – SCLP header) */ ++ __u8 data[0]; /* Subsequent Data passed verbatim to SCLP ET 24 */ ++}; ++ ++struct zpci_report_error_data { ++ __u64 timestamp; ++ __u64 err_log_id; ++ char log_data[4054]; /* We cannot exceed a total of 4074 bytes (header + data) */ ++}; ++ ++struct zpci_report_error { ++ struct zpci_report_error_header header; ++ struct zpci_report_error_data data; ++} __packed; ++ ++#endif /* ZPCICTL_H */ diff --git a/s390-tools-sles15sp1-05-cpumf-add-missing-Description-tag-for-z13-z14-ctr-12.patch b/s390-tools-sles15sp1-05-cpumf-add-missing-Description-tag-for-z13-z14-ctr-12.patch new file mode 100644 index 0000000..01f3e19 --- /dev/null +++ b/s390-tools-sles15sp1-05-cpumf-add-missing-Description-tag-for-z13-z14-ctr-12.patch @@ -0,0 +1,42 @@ +Subject: cpumf: add missing Description: tag for z13/z14/ctr:128 +From: Hendrik Brueckner + +Summary: cpumf: Add CPU-MF hardware counters for z14 +Description: Add hardware counter definitions for IBM z14. +Upstream-ID: a3c746846d86ebcee6cbf36505598b7da367665b +Problem-ID: KRN1608 + +Upstream-Description: + + cpumf: add missing Description: tag for z13/z14/ctr:128 + + Signed-off-by: Thomas Richter + Signed-off-by: Jan Höppner + + +Signed-off-by: Hendrik Brueckner +--- + cpumf/data/cpum-cf-extended-z13.ctr | 1 + + cpumf/data/cpum-cf-extended-z14.ctr | 1 + + 2 files changed, 2 insertions(+) + +--- a/cpumf/data/cpum-cf-extended-z13.ctr ++++ b/cpumf/data/cpum-cf-extended-z13.ctr +@@ -17,6 +17,7 @@ + # Extended Counter Set + # --------------------------------------------------------------------- + Counter:128 Name:L1D_WRITES_RO_EXCL ++Description: + A directory write to the Level-1 Data cache where the line was + originally in a Read-Only state in the cache but has been updated + to be in the Exclusive state that allows stores to the cache line. +--- a/cpumf/data/cpum-cf-extended-z14.ctr ++++ b/cpumf/data/cpum-cf-extended-z14.ctr +@@ -20,6 +20,7 @@ + # Extended Counter Set + # --------------------------------------------------------------------- + Counter:128 Name:L1D_WRITES_RO_EXCL ++Description: + A directory write to the Level-1 Data cache where the line was + originally in a Read-Only state in the cache but has been updated + to be in the Exclusive state that allows stores to the cache line diff --git a/s390-tools-sles15sp1-05-zpcictl-include-sys-sysmacros.h-to-avoid-minor-major.patch b/s390-tools-sles15sp1-05-zpcictl-include-sys-sysmacros.h-to-avoid-minor-major.patch new file mode 100644 index 0000000..45e4dd3 --- /dev/null +++ b/s390-tools-sles15sp1-05-zpcictl-include-sys-sysmacros.h-to-avoid-minor-major.patch @@ -0,0 +1,48 @@ +Subject: zpcictl: include sys/sysmacros.h to avoid minor/major glibc warnings +From: Jan Hoeppner + +Summary: zpcictl: Add tool to manage PCI devices +Description: Use the zpcictl tool to manage PCI devices on the IBM Z + platform. Initial functions include generating firmware + error logs, resetting PCI devices, and preparing a device + for further repair actions. +Upstream-ID: f35c5d01fd04ecf019f31c58edc0c5165ad276ad +Problem-ID: RAS1703 + +Upstream-Description: + + zpcictl: include sys/sysmacros.h to avoid minor/major glibc warnings + + The minor()/major() function definitions are moved to sys/sysmacros.h + and will be removed from sys/types.h. To correct below warning, simply + include sys/sysmacros.h. + + zpcictl.c: In function ‘sysfs_get_slot_addr’: + zpcictl.c:184:13: warning: In the GNU C Library, "major" is defined + by . For historical compatibility, it is + currently defined by as well, but we plan to + remove this soon. To use "major", include + directly. If you did not intend to use a system-defined macro + "major", you should undefine it after including . + major = major(dev_stat.st_rdev); + ^~~~~~~~~~~~~~~~~~~~~ + + Signed-off-by: Hendrik Brueckner + Signed-off-by: Stefan Haberland + + +Signed-off-by: Jan Hoeppner +--- + zpcictl/zpcictl.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/zpcictl/zpcictl.c ++++ b/zpcictl/zpcictl.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + #include + + #include "lib/util_base.h" diff --git a/s390-tools-sles15sp1-06-cpumf-correct-counter-name-for-z13-and-z14.patch b/s390-tools-sles15sp1-06-cpumf-correct-counter-name-for-z13-and-z14.patch new file mode 100644 index 0000000..c629e53 --- /dev/null +++ b/s390-tools-sles15sp1-06-cpumf-correct-counter-name-for-z13-and-z14.patch @@ -0,0 +1,44 @@ +Subject: cpumf: correct counter name for z13 and z14 +From: Hendrik Brueckner + +Summary: cpumf: Add CPU-MF hardware counters for z14 +Description: Add hardware counter definitions for IBM z14. +Upstream-ID: 9745e4678adf18869e661d13f2b666a929450fa1 +Problem-ID: KRN1608 + +Upstream-Description: + + cpumf: correct counter name for z13 and z14 + + Signed-off-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Hendrik Brueckner +--- + cpumf/data/cpum-cf-extended-z13.ctr | 2 +- + cpumf/data/cpum-cf-extended-z14.ctr | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/cpumf/data/cpum-cf-extended-z13.ctr ++++ b/cpumf/data/cpum-cf-extended-z13.ctr +@@ -16,7 +16,7 @@ + # + # Extended Counter Set + # --------------------------------------------------------------------- +-Counter:128 Name:L1D_WRITES_RO_EXCL ++Counter:128 Name:L1D_RO_EXCL_WRITES + Description: + A directory write to the Level-1 Data cache where the line was + originally in a Read-Only state in the cache but has been updated +--- a/cpumf/data/cpum-cf-extended-z14.ctr ++++ b/cpumf/data/cpum-cf-extended-z14.ctr +@@ -19,7 +19,7 @@ + # + # Extended Counter Set + # --------------------------------------------------------------------- +-Counter:128 Name:L1D_WRITES_RO_EXCL ++Counter:128 Name:L1D_RO_EXCL_WRITES + Description: + A directory write to the Level-1 Data cache where the line was + originally in a Read-Only state in the cache but has been updated diff --git a/s390-tools-sles15sp1-06-zpcictl-Rephrase-man-page-entries-and-tool-output.patch b/s390-tools-sles15sp1-06-zpcictl-Rephrase-man-page-entries-and-tool-output.patch new file mode 100644 index 0000000..b355098 --- /dev/null +++ b/s390-tools-sles15sp1-06-zpcictl-Rephrase-man-page-entries-and-tool-output.patch @@ -0,0 +1,91 @@ +Subject: zpcictl: Rephrase man page entries and tool output +From: Jan Hoeppner + +Summary: zpcictl: Add tool to manage PCI devices +Description: Use the zpcictl tool to manage PCI devices on the IBM Z + platform. Initial functions include generating firmware + error logs, resetting PCI devices, and preparing a device + for further repair actions. +Upstream-ID: d03be735366de57be0c642f6f21b06b1f2df6a6e +Problem-ID: RAS1703 + +Upstream-Description: + + zpcictl: Rephrase man page entries and tool output + + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Jan Hoeppner +--- + zpcictl/zpcictl.8 | 13 ++++++++----- + zpcictl/zpcictl.c | 9 +++++---- + 2 files changed, 13 insertions(+), 9 deletions(-) + +--- a/zpcictl/zpcictl.8 ++++ b/zpcictl/zpcictl.8 +@@ -1,4 +1,4 @@ +-.\" Copyright 2017 IBM Corp. ++.\" Copyright IBM Corp. 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. + .\" +@@ -30,9 +30,10 @@ zpcictl - Manage PCI devices on z System + . + . + .SH DESCRIPTION ++With + .B zpcictl +-is a tool for managing PCI devices on the IBM z Systems platform. It is +-especially used for reporting errorneous PCI devices to the service element. ++, you can manage PCI devices on the IBM z Systems platform. It is especially ++used for reporting erroneous PCI devices to the service element. + + .B Note: + For NVMe devices additional data (such as S.M.A.R.T. data) is collected and sent +@@ -44,7 +45,9 @@ for this to work. + .SH DEVICE + .B DEVICE + can be either the PCI slot address (e.g. 0000:00:00.0) or the main device node +-of an NVMe device (e.g. /dev/nvme0). ++of an NVMe device (e.g. ++.I /dev/nvme0 ++). + . + . + .SH OPTIONS +@@ -52,7 +55,7 @@ of an NVMe device (e.g. /dev/nvme0). + .OD reset "" "DEVICE" + Reset + .I DEVICE +-and initiate a re-initialisation of the adapter. ++and initiate a re-initialization of the PCI device. + .PP + . + .OD deconfigure "" "DEVICE" +--- a/zpcictl/zpcictl.c ++++ b/zpcictl/zpcictl.c +@@ -240,7 +240,7 @@ static int device_exists(char *dev) + static void get_device_info(struct zpci_device *pdev, char *dev) + { + if (!device_exists(dev)) +- errx(EXIT_FAILURE, "Device %s not found", dev); ++ errx(EXIT_FAILURE, "Could not find device %s", dev); + if (is_blk_dev(dev)) + errx(EXIT_FAILURE, "Unsupported device type %s", dev); + if (is_char_dev(dev)) { +@@ -254,9 +254,10 @@ static void get_device_info(struct zpci_ + pdev->fid = sysfs_read_value(pdev, "function_id"); + pdev->pchid = sysfs_read_value(pdev, "pchid"); + +- /* In case a slot address was specified, we still need to figure out +- * the device node for NVMe devices. Otherwise we won't be able to +- * collect S.M.A.R.T. data at a later point. ++ /* ++ * In case a slot address was specified, the device node for NVMe ++ * devices is still needed. Otherwise it won't be possible to collect ++ * S.M.A.R.T. data at a later point. + */ + if (!pdev->device && pdev->class == PCI_CLASS_NVME) + get_device_node(pdev); diff --git a/s390-tools-sles15sp1-07-cpumf-Add-IBM-z14-ZR1-to-the-CPU-Measurement-Facilit.patch b/s390-tools-sles15sp1-07-cpumf-Add-IBM-z14-ZR1-to-the-CPU-Measurement-Facilit.patch new file mode 100644 index 0000000..6a1d761 --- /dev/null +++ b/s390-tools-sles15sp1-07-cpumf-Add-IBM-z14-ZR1-to-the-CPU-Measurement-Facilit.patch @@ -0,0 +1,41 @@ +Subject: cpumf: Add IBM z14 ZR1 to the CPU Measurement Facility model list +From: Hendrik Brueckner + +Summary: cpumf: Add CPU-MF hardware counters for z14 +Description: Add hardware counter definitions for IBM z14. +Upstream-ID: f642019bcc17370231666e772c7e4cec19f1dfdc +Problem-ID: KRN1608 + +Upstream-Description: + + cpumf: Add IBM z14 ZR1 to the CPU Measurement Facility model list + + Signed-off-by: Thomas Richter + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Hendrik Brueckner +--- + cpumf/bin/cpumf_helper.in | 1 + + cpumf/data/cpum-cf-hw-counter.map | 1 + + 2 files changed, 2 insertions(+) + +--- a/cpumf/bin/cpumf_helper.in ++++ b/cpumf/bin/cpumf_helper.in +@@ -211,6 +211,7 @@ my $system_z_hwtype_map = { + 2964 => 'IBM z13', + 2965 => 'IBM z13s', + 3906 => 'IBM z14', ++ 3907 => 'IBM z14 ZR1', + }; + + sub get_hardware_type() +--- a/cpumf/data/cpum-cf-hw-counter.map ++++ b/cpumf/data/cpum-cf-hw-counter.map +@@ -26,4 +26,5 @@ + 2964 => 'cpum-cf-extended-z13.ctr', + 2965 => 'cpum-cf-extended-z13.ctr', + 3906 => 'cpum-cf-extended-z14.ctr', ++ 3907 => 'cpum-cf-extended-z14.ctr', + }; diff --git a/s390-tools-sles15sp1-07-zpcictl-Use-fopen-instead-of-open-for-writes.patch b/s390-tools-sles15sp1-07-zpcictl-Use-fopen-instead-of-open-for-writes.patch new file mode 100644 index 0000000..2f8cc6a --- /dev/null +++ b/s390-tools-sles15sp1-07-zpcictl-Use-fopen-instead-of-open-for-writes.patch @@ -0,0 +1,55 @@ +Subject: zpcictl: Use fopen() instead of open() for writes +From: Jan Hoeppner + +Summary: zpcictl: Add tool to manage PCI devices +Description: Use the zpcictl tool to manage PCI devices on the IBM Z + platform. Initial functions include generating firmware + error logs, resetting PCI devices, and preparing a device + for further repair actions. +Upstream-ID: 8f0496b26aae88e206ac9a95b317043e78d147b8 +Problem-ID: RAS1703 + +Upstream-Description: + + zpcictl: Use fopen() instead of open() for writes + + Be consistent with the rest of the code and use fopen() rather than + open(). + + Reviewed-by: Hendrik Brueckner + Signed-off-by: Jan Höppner + + +Signed-off-by: Jan Hoeppner +--- + zpcictl/zpcictl.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +--- a/zpcictl/zpcictl.c ++++ b/zpcictl/zpcictl.c +@@ -155,17 +155,19 @@ static unsigned int sysfs_read_value(str + + static void sysfs_write_data(struct zpci_report_error *report, char *slot) + { ++ size_t r_size; + char *path; +- int fd, rc; ++ FILE *fp; ++ ++ r_size = sizeof(*report); + + path = util_path_sysfs("bus/pci/devices/%s/report_error", slot); +- fd = open(path, O_WRONLY); +- if (!fd) ++ fp = fopen(path, "w"); ++ if (!fp) + fopen_err(path); +- rc = write(fd, report, sizeof(*report)); +- if (rc == -1) ++ if (fwrite(report, 1, r_size, fp) != r_size) + warnx("Could not write to file: %s: %s", path, strerror(errno)); +- if (close(fd)) ++ if (fclose(fp)) + warnx("Could not close file: %s: %s", path, strerror(errno)); + free(path); + } diff --git a/s390-tools-sles15sp1-08-zpcictl-Read-device-link-to-obtain-device-address.patch b/s390-tools-sles15sp1-08-zpcictl-Read-device-link-to-obtain-device-address.patch new file mode 100644 index 0000000..5c1e2f4 --- /dev/null +++ b/s390-tools-sles15sp1-08-zpcictl-Read-device-link-to-obtain-device-address.patch @@ -0,0 +1,77 @@ +Subject: zpcictl: Read device link to obtain device address +From: Jan Hoeppner + +Summary: zpcictl: Add tool to manage PCI devices +Description: Use the zpcictl tool to manage PCI devices on the IBM Z + platform. Initial functions include generating firmware + error logs, resetting PCI devices, and preparing a device + for further repair actions. +Upstream-ID: e2a8d85916fb77d2a9b41253446973cd97107c42 +Problem-ID: RAS1703 + +Upstream-Description: + + zpcictl: Read device link to obtain device address + + The address sysfs attribute might not be present on some older kernel + levels. Read the device link instead using readlink() to obtain the + address. + + Signed-off-by: Jan Höppner + + +Signed-off-by: Jan Hoeppner +--- + zpcictl/zpcictl.c | 27 ++++++++++++++++++--------- + 1 file changed, 18 insertions(+), 9 deletions(-) + +--- a/zpcictl/zpcictl.c ++++ b/zpcictl/zpcictl.c +@@ -172,13 +172,16 @@ static void sysfs_write_data(struct zpci + free(path); + } + ++/* lstat() doesn't work for sysfs files, so we have to work with a fixed size */ ++#define READLINK_SIZE 256 ++ + static void sysfs_get_slot_addr(const char *dev, char *slot) + { ++ char device[READLINK_SIZE], *result; + unsigned int major, minor; + struct stat dev_stat; +- char addr[13]; ++ ssize_t len; + char *path; +- FILE *fp; + + if (stat(dev, &dev_stat) != 0) { + errx(EXIT_FAILURE, "Could not get stat information for %s: %s", +@@ -187,15 +190,21 @@ static void sysfs_get_slot_addr(const ch + major = major(dev_stat.st_rdev); + minor = minor(dev_stat.st_rdev); + +- path = util_path_sysfs("dev/char/%u:%u/address", major, minor); +- fp = fopen(path, "r"); +- if (!fp) +- fopen_err(path); +- fscanf(fp, "%s", addr); +- fclose(fp); ++ path = util_path_sysfs("dev/char/%u:%u/device", major, minor); ++ len = readlink(path, device, READLINK_SIZE - 1); + free(path); ++ if (len != -1) ++ device[len] = '\0'; ++ else ++ errx(EXIT_FAILURE, "Could not read device link for %s", dev); ++ ++ result = strrchr(device, '/'); ++ if (result) ++ result++; ++ else ++ result = device; + +- strcpy(slot, addr); ++ strcpy(slot, result); + } + + static void get_device_node(struct zpci_device *pdev) diff --git a/s390-tools-sles15sp1-09-zpcictl-Make-device-node-for-NVMe-optional.patch b/s390-tools-sles15sp1-09-zpcictl-Make-device-node-for-NVMe-optional.patch new file mode 100644 index 0000000..eaa780b --- /dev/null +++ b/s390-tools-sles15sp1-09-zpcictl-Make-device-node-for-NVMe-optional.patch @@ -0,0 +1,124 @@ +Subject: zpcictl: Make device node for NVMe optional +From: Jan Hoeppner + +Summary: zpcictl: Add tool to manage PCI devices +Description: Use the zpcictl tool to manage PCI devices on the IBM Z + platform. Initial functions include generating firmware + error logs, resetting PCI devices, and preparing a device + for further repair actions. +Upstream-ID: 342c6a3707315514f0f886fabb532f6c8b59b694 +Problem-ID: RAS1703 + +Upstream-Description: + + zpcictl: Make device node for NVMe optional + + At the moment, if we specify the slot address of an NVMe device but + can't find the corresponding device node, the execution is terminated. + + This is a bit harsh as the device node is rather optional and only + necessary to collect S.M.A.R.T. data. We should still be able to issue + the error reporting, even if we couldn't determine the device node. + + Therefore, make sure the device node for NVMe devices is optional by + changing various error messages to warnings. + Change sysfs_get_slot_addr() to have a return value and work with that + accordingly. + Also make sure, that execution is terminated when a valid device node + was specified but no matching slot address was determined. The slot + address is necessary to issue the error reporting commands. + + Signed-off-by: Jan Höppner + + +Signed-off-by: Jan Hoeppner +--- + zpcictl/zpcictl.c | 30 ++++++++++++++++++++---------- + 1 file changed, 20 insertions(+), 10 deletions(-) + +--- a/zpcictl/zpcictl.c ++++ b/zpcictl/zpcictl.c +@@ -104,6 +104,9 @@ static char *collect_smart_data(struct z + char *cmd; + FILE *fd; + ++ if (!pdev->device) ++ return NULL; ++ + util_asprintf(&cmd, SMARTCTL_CMDLINE, pdev->device); + fd = popen(cmd, "r"); + if (!fd) +@@ -175,7 +178,7 @@ static void sysfs_write_data(struct zpci + /* lstat() doesn't work for sysfs files, so we have to work with a fixed size */ + #define READLINK_SIZE 256 + +-static void sysfs_get_slot_addr(const char *dev, char *slot) ++static int sysfs_get_slot_addr(const char *dev, char *slot) + { + char device[READLINK_SIZE], *result; + unsigned int major, minor; +@@ -184,8 +187,9 @@ static void sysfs_get_slot_addr(const ch + char *path; + + if (stat(dev, &dev_stat) != 0) { +- errx(EXIT_FAILURE, "Could not get stat information for %s: %s", +- dev, strerror(errno)); ++ warnx("Could not get stat information for %s: %s", ++ dev, strerror(errno)); ++ return 0; + } + major = major(dev_stat.st_rdev); + minor = minor(dev_stat.st_rdev); +@@ -193,18 +197,21 @@ static void sysfs_get_slot_addr(const ch + path = util_path_sysfs("dev/char/%u:%u/device", major, minor); + len = readlink(path, device, READLINK_SIZE - 1); + free(path); +- if (len != -1) ++ if (len != -1) { + device[len] = '\0'; +- else +- errx(EXIT_FAILURE, "Could not read device link for %s", dev); ++ } else { ++ warnx("Could not read device link for %s", dev); ++ return 0; ++ } + + result = strrchr(device, '/'); + if (result) + result++; + else + result = device; +- + strcpy(slot, result); ++ ++ return 1; + } + + static void get_device_node(struct zpci_device *pdev) +@@ -219,12 +226,13 @@ static void get_device_node(struct zpci_ + if (count == -1) { + warnx("Could not read directory %s: %s", path, strerror(errno)); + free(path); +- exit(EXIT_FAILURE); ++ return; + } + + for (i = 0; i < count; i++) { + util_asprintf(&dev, "/dev/%s", de_vec[i]->d_name); +- sysfs_get_slot_addr(dev, slot); ++ if (!sysfs_get_slot_addr(dev, slot)) ++ continue; + if (strcmp(slot, pdev->slot) == 0) { + pdev->device = dev; + break; +@@ -255,7 +263,9 @@ static void get_device_info(struct zpci_ + if (is_blk_dev(dev)) + errx(EXIT_FAILURE, "Unsupported device type %s", dev); + if (is_char_dev(dev)) { +- sysfs_get_slot_addr(dev, pdev->slot); ++ if (!sysfs_get_slot_addr(dev, pdev->slot)) ++ errx(EXIT_FAILURE, ++ "Could not determine slot address for %s", dev); + pdev->device = dev; + } else { + strcpy(pdev->slot, dev); diff --git a/s390-tools-sles15sp1-10-zpcictl-Change-wording-of-man-page-and-help-output.patch b/s390-tools-sles15sp1-10-zpcictl-Change-wording-of-man-page-and-help-output.patch new file mode 100644 index 0000000..1c728e9 --- /dev/null +++ b/s390-tools-sles15sp1-10-zpcictl-Change-wording-of-man-page-and-help-output.patch @@ -0,0 +1,143 @@ +Subject: zpcictl: Change wording of man-page and help output +From: Jan Hoeppner + +Summary: zpcictl: Add tool to manage PCI devices +Description: Use the zpcictl tool to manage PCI devices on the IBM Z + platform. Initial functions include generating firmware + error logs, resetting PCI devices, and preparing a device + for further repair actions. +Upstream-ID: aaaebb2030c80151ecac528f22cb9a52752b868c +Problem-ID: RAS1703 + +Upstream-Description: + + zpcictl: Change wording of man-page and help output + + Signed-off-by: Jan Höppner + + +Signed-off-by: Jan Hoeppner +--- + zpcictl/zpcictl.8 | 38 +++++++++++++++----------------------- + zpcictl/zpcictl.c | 15 ++++++++------- + 2 files changed, 23 insertions(+), 30 deletions(-) + +--- a/zpcictl/zpcictl.8 ++++ b/zpcictl/zpcictl.8 +@@ -20,7 +20,7 @@ + .TH zpcictl 8 "Oct 2018" s390-tools zpcictl + . + .SH NAME +-zpcictl - Manage PCI devices on z Systems ++zpcictl - Manage PCI devices on IBM Z + . + . + .SH SYNOPSIS +@@ -30,50 +30,42 @@ zpcictl - Manage PCI devices on z System + . + . + .SH DESCRIPTION +-With ++Use + .B zpcictl +-, you can manage PCI devices on the IBM z Systems platform. It is especially +-used for reporting erroneous PCI devices to the service element. ++to manage PCI devices on the IBM Z platform. In particular, ++use this command to report defective PCI devices to the service element. + + .B Note: + For NVMe devices additional data (such as S.M.A.R.T. data) is collected and sent +-with any error handling action. The smartmontools are required to be installed +-for this to work. ++with any error handling action. For this extendend data collection, the ++smartmontools must be installed. + .PP + . + . + .SH DEVICE +-.B DEVICE +-can be either the PCI slot address (e.g. 0000:00:00.0) or the main device node +-of an NVMe device (e.g. ++A PCI slot address (e.g. 0000:00:00.0) or the main device node of an NVMe ++device (e.g. + .I /dev/nvme0 + ). + . + . + .SH OPTIONS +-.SS Error Handling ++.SS Error Handling Options + .OD reset "" "DEVICE" +-Reset +-.I DEVICE +-and initiate a re-initialization of the PCI device. ++Reset and re-initialize the PCI device. + .PP + . + .OD deconfigure "" "DEVICE" +-De-configure +-.I DEVICE +-and prepare for any repair action. This action will move the +-PCI device from a configured to a reserved state. ++Deconfigure the PCI device and prepare for any repair action. This action ++changes the status of the PCI device from configured to reserved. + .PP + . + .OD report-error "" "DEVICE" +-Report any device error for +-.IR DEVICE . +-The +-.I DEVICE +-is marked as erroneous and no further action is initiated on it. ++Report any device error for the PCI device. ++The device is marked as defective but no further action is taken. + .PP + . +-.SS Misc ++.SS General Options + .OD help "h" "" + Print usage information, then exit. + .PP +--- a/zpcictl/zpcictl.c ++++ b/zpcictl/zpcictl.c +@@ -27,8 +27,9 @@ + #define SMARTCTL_CMDLINE "smartctl -x %s 2>/dev/null" + + static const struct util_prg prg = { +- .desc = "Use zpcictl to manage PCI devices on s390\n" +- "DEVICE is the slot id or node of the device (e.g. /dev/nvme0)", ++ .desc = "Use zpcictl to manage PCI devices on IBM Z\n" ++ "DEVICE is the slot ID or node of the device " ++ "(e.g. 0000:00:00.0 or /dev/nvme0)", + .args = "DEVICE", + .copyright_vec = { + { +@@ -46,23 +47,23 @@ static const struct util_prg prg = { + #define OPT_REPORT_ERR 130 + + static struct util_opt opt_vec[] = { +- UTIL_OPT_SECTION("ERROR HANDLING"), ++ UTIL_OPT_SECTION("ERROR HANDLING OPTIONS"), + { + .option = { "reset", no_argument, NULL, OPT_RESET }, +- .desc = "Reset device", ++ .desc = "Reset the device", + .flags = UTIL_OPT_FLAG_NOSHORT, + }, + { + .option = { "deconfigure", no_argument, NULL, OPT_DECONF }, +- .desc = "De-configure device and prepare for any repair action", ++ .desc = "Deconfigure the device to prepare for any repair action", + .flags = UTIL_OPT_FLAG_NOSHORT, + }, + { + .option = { "report-error", no_argument, NULL, OPT_REPORT_ERR }, +- .desc = "Report device error to service element (SE)", ++ .desc = "Report a device error to the service element (SE)", + .flags = UTIL_OPT_FLAG_NOSHORT, + }, +- UTIL_OPT_SECTION("MISC"), ++ UTIL_OPT_SECTION("GENERAL OPTIONS"), + UTIL_OPT_HELP, + UTIL_OPT_VERSION, + UTIL_OPT_END diff --git a/s390-tools-sles15sp1-dbginfo-gather-nvme-related-data.patch b/s390-tools-sles15sp1-dbginfo-gather-nvme-related-data.patch new file mode 100644 index 0000000..d16f4b0 --- /dev/null +++ b/s390-tools-sles15sp1-dbginfo-gather-nvme-related-data.patch @@ -0,0 +1,75 @@ +Subject: dbginfo: gather nvme related data +From: Sebastian Ott + +Summary: s390-tools/dbginfo: Collect NVMe-related debug data +Description: Collect SMART (Self-Monitoring, Analysis and Reporting Technology) + data in dbginfo.sh . +Upstream-ID: b9e47e356bbfc92e41b758e74606baacbab33ee4 +Problem-ID: RAS1702 + +Upstream-Description: + + dbginfo: gather nvme related data + + Signed-off-by: Sebastian Ott + Signed-off-by: Stefan Haberland + + +Signed-off-by: Sebastian Ott +--- + scripts/dbginfo.sh | 26 +++++++++++++++++++++++++- + 1 file changed, 25 insertions(+), 1 deletion(-) + +--- a/scripts/dbginfo.sh ++++ b/scripts/dbginfo.sh +@@ -182,11 +182,14 @@ readonly OUTPUT_FILE_XML="${WORKPATH}dom + # File that includes the docker inspect output + readonly OUTPUT_FILE_DOCKER="${WORKPATH}docker_inspect.out" + ++# File that includes nvme related information ++readonly OUTPUT_FILE_NVME="${WORKPATH}nvme.out" ++ + # Mount point of the debug file system + readonly MOUNT_POINT_DEBUGFS="/sys/kernel/debug" + + # The amount of steps running the whole collections +-readonly COLLECTION_COUNT=11 ++readonly COLLECTION_COUNT=12 + + # The kernel version (e.g. '2' from 2.6.32 or '3' from 3.2.1) + readonly KERNEL_VERSION=$(uname -r 2>/dev/null | cut -d'.' -f1) +@@ -829,6 +832,25 @@ collect_docker() { + } + + ######################################## ++collect_nvme() { ++ local NVME ++ ++ pr_syslog_stdout "11 of ${COLLECTION_COUNT}: Collecting nvme output" ++ call_run_command "nvme list" "${OUTPUT_FILE_NVME}" ++ ++ for NVME in /dev/nvme[0-9]*; do ++ if [ -c $NVME ]; then ++ call_run_command "smartctl -x $NVME" "${OUTPUT_FILE_NVME}" ++ call_run_command "nvme fw-log $NVME" "${OUTPUT_FILE_NVME}" ++ call_run_command "nvme smart-log $NVME" "${OUTPUT_FILE_NVME}" ++ call_run_command "nvme error-log $NVME" "${OUTPUT_FILE_NVME}" ++ fi ++ done ++ ++ pr_log_stdout " " ++} ++ ++######################################## + post_processing() { + local file_mtime + local file_mtime_epoche +@@ -1120,6 +1142,8 @@ collect_domain_xml + + collect_docker + ++collect_nvme ++ + post_processing + + create_package diff --git a/s390-tools.changes b/s390-tools.changes index e6ab8d2..576cf22 100644 --- a/s390-tools.changes +++ b/s390-tools.changes @@ -1,19 +1,76 @@ ------------------------------------------------------------------- -Mon Oct 22 19:44:05 UTC 2018 - mpost@suse.com +Tue Nov 13 19:22:01 UTC 2018 - mpost@suse.com - Added s390-tools-sles15-zdev-fix-qeth-BridgePort-and-VNICC-conflict-checking.patch - zdev: qeth BridgePort and VNICC attribute conflict (bsc#1112536) + (bsc#1112536) + zdev: qeth BridgePort and VNICC attribute conflict +- Added the following patches for Fate#326376 (bsc#1113321) + * s390-tools-sles15sp1-01-util_path-add-function-to-check-if-a-path-exists.patch + * s390-tools-sles15sp1-02-util_path-Add-description-for-util_path_exists.patch + * s390-tools-sles15sp1-03-util_path-Make-true-false-handling-consistent-with-o.patch + * s390-tools-sles15sp1-04-zpcictl-Introduce-new-tool-zpcictl.patch + * s390-tools-sles15sp1-05-zpcictl-include-sys-sysmacros.h-to-avoid-minor-major.patch + * s390-tools-sles15sp1-06-zpcictl-Rephrase-man-page-entries-and-tool-output.patch + * s390-tools-sles15sp1-07-zpcictl-Use-fopen-instead-of-open-for-writes.patch + * s390-tools-sles15sp1-08-zpcictl-Read-device-link-to-obtain-device-address.patch + * s390-tools-sles15sp1-09-zpcictl-Make-device-node-for-NVMe-optional.patch + * s390-tools-sles15sp1-10-zpcictl-Change-wording-of-man-page-and-help-output.patch +- Added the following patches for Fate#325684 (bsc#1113323) + * s390-tools-sles15sp1-0001-zkey-Add-properties-file-handling-routines.patch + * s390-tools-sles15sp1-0002-zkey-Add-build-dependency-to-OpenSSL-libcrypto.patch + * s390-tools-sles15sp1-0003-zkey-Add-helper-functions-for-comma-separated-string.patch + * s390-tools-sles15sp1-0004-zkey-Externalize-secure-key-back-end-functions.patch + * s390-tools-sles15sp1-0005-zkey-Add-keystore-implementation.patch + * s390-tools-sles15sp1-0006-zkey-Add-keystore-related-commands.patch + * s390-tools-sles15sp1-0007-zkey-Create-key-repository-and-group-during-make-ins.patch + * s390-tools-sles15sp1-0008-zkey-Man-page-updates.patch + * s390-tools-sles15sp1-0009-zkey-let-packaging-create-the-zkeyadm-group-and-perm.patch + * s390-tools-sles15sp1-0010-zkey-Update-README-to-add-info-about-packaging-requi.patch +- Added the following patches for Fate#326390 (bsc#1113353) + * s390-tools-sles15sp1-0011-zkey-Typo-in-message.patch + * s390-tools-sles15sp1-0012-zkey-Fix-memory-leak.patch + * s390-tools-sles15sp1-0013-zkey-Fix-APQN-validation-routine.patch + * s390-tools-sles15sp1-0014-zkey-Fix-generate-and-import-leaving-key-in-an-incon.patch + * s390-tools-sles15sp1-0015-zkey-Add-zkey-cryptsetup-tool.patch + * s390-tools-sles15sp1-0016-zkey-Add-man-page-for-zkey-cryptsetup.patch + * s390-tools-sles15sp1-0017-zkey-Add-build-dependency-for-libcryptsetup-and-json.patch + * s390-tools-sles15sp1-0018-zkey-Add-key-verification-pattern-property.patch + * s390-tools-sles15sp1-0019-zkey-Add-volume-type-property-to-support-LUKS2-volum.patch +- Added the following patches for Fate#325691 (bsc#1113324) + * s390-tools-sles15sp1-01-lszcrypt-CEX6S-exploitation.patch + * s390-tools-sles15sp1-02-lszcrypt-fix-date-and-wrong-indentation.patch +- Added the following patches for Fate#326388 (bsc#1113331) + * s390-tools-sles15sp1-01-cpumf-Add-extended-counter-defintion-files-for-IBM-z.patch + * s390-tools-sles15sp1-02-cpumf-z14-split-counter-sets-according-to-CFVN-CSVN-.patch + * s390-tools-sles15sp1-03-cpumf-cpumf_helper-read-split-counter-sets-part-2-2.patch + * s390-tools-sles15sp1-04-cpumf-correct-z14-counter-number.patch + * s390-tools-sles15sp1-05-cpumf-add-missing-Description-tag-for-z13-z14-ctr-12.patch + * s390-tools-sles15sp1-06-cpumf-correct-counter-name-for-z13-and-z14.patch + * s390-tools-sles15sp1-07-cpumf-Add-IBM-z14-ZR1-to-the-CPU-Measurement-Facilit.patch +- Added the following patch for Fate#326361 (bsc#1113333) + * s390-tools-sles15sp1-dbginfo-gather-nvme-related-data.patch +- Temporarily added "HAVE_CRYPTSETUP2=0" to the make and make install + commands, because a couple of Fate requests have not been approved + yet, resulting in build failure. +- Added "Recommends: blktrace" to the spec file (bsc#1112855) +- Changed remaining insserv references to systemd entries. +- Changed the Group from the obsolete "System Environment/Base" to + "System/Base." ------------------------------------------------------------------- Fri Aug 31 18:57:54 UTC 2018 - mpost@suse.com -- Added the following patch for bsc#1094354 +- Added the following patch to remove the call to zipl for bsc#1094354 * customize-zdev-root-update-script.patch - Modified ctc_configure to not pass a "protcol=" parameter when configuring LCS devices. (bsc#1096520) -- Added the following patches for bsc#1098069 - * s390-tools-sles15-dbginfo-add-data-for-ps-cpprot.patch - * s390-tools-sles15-mon_procd-fix-parsing-of-proc-pid-stat.patch +- Added the following two patches for bsc#1098069 + * dbginfo.sh: Extend data collection + s390-tools-sles15-dbginfo-add-data-for-ps-cpprot.patch + * mon_procd: fix parsing of /proc//stat + s390-tools-sles15-mon_procd-fix-parsing-of-proc-pid-stat.patch +- Added the following patches for "lstape, lsluns: handle non-zfcp; + lin_tape multiple paths" (bsc#1098069) * s390-tools-sles15-1-lstape-fix-output-with-SCSI-lin_tape-and-multiple-pa.patch * s390-tools-sles15-2-lstape-fix-to-prefer-sysfs-to-find-lin_tape-device-n.patch * s390-tools-sles15-3-lstape-fix-output-without-SCSI-generic-sg.patch diff --git a/s390-tools.spec b/s390-tools.spec index 733d015..f5fedb1 100644 --- a/s390-tools.spec +++ b/s390-tools.spec @@ -40,7 +40,7 @@ BuildRequires: net-snmp-devel BuildRequires: qclib-devel-static BuildRequires: tcpd-devel BuildRequires: zlib-devel-static -PreReq: shadow %insserv_prereq %fillup_prereq dracut permissions +PreReq: shadow %fillup_prereq dracut permissions Requires: coreutils Requires: gawk Requires: perl-base @@ -49,6 +49,7 @@ Requires: rsync Requires: tar Requires: util-linux Provides: s390utils:/sbin/dasdfmt +Recommends: blktrace # Don't build with pie to avoid problems with zipl #!BuildIgnore: gcc-PIE Source: s390-tools-%{version}.tar.gz @@ -152,6 +153,45 @@ Patch41: s390-tools-sles15-6-lstape-fix-description-of-type-and-devbusid- Patch42: s390-tools-sles15-7-lstape-fix-SCSI-output-description-in-man-page.patch Patch43: s390-tools-sles15-8-lstape-fix-SCSI-HBA-CCW-device-bus-ID-e.g.-for-virti.patch Patch44: s390-tools-sles15-zdev-fix-qeth-BridgePort-and-VNICC-conflict-checking.patch +Patch45: s390-tools-sles15sp1-01-util_path-add-function-to-check-if-a-path-exists.patch +Patch46: s390-tools-sles15sp1-02-util_path-Add-description-for-util_path_exists.patch +Patch47: s390-tools-sles15sp1-03-util_path-Make-true-false-handling-consistent-with-o.patch +Patch48: s390-tools-sles15sp1-04-zpcictl-Introduce-new-tool-zpcictl.patch +Patch49: s390-tools-sles15sp1-05-zpcictl-include-sys-sysmacros.h-to-avoid-minor-major.patch +Patch50: s390-tools-sles15sp1-06-zpcictl-Rephrase-man-page-entries-and-tool-output.patch +Patch51: s390-tools-sles15sp1-07-zpcictl-Use-fopen-instead-of-open-for-writes.patch +Patch52: s390-tools-sles15sp1-08-zpcictl-Read-device-link-to-obtain-device-address.patch +Patch53: s390-tools-sles15sp1-09-zpcictl-Make-device-node-for-NVMe-optional.patch +Patch54: s390-tools-sles15sp1-10-zpcictl-Change-wording-of-man-page-and-help-output.patch +Patch55: s390-tools-sles15sp1-0001-zkey-Add-properties-file-handling-routines.patch +Patch56: s390-tools-sles15sp1-0002-zkey-Add-build-dependency-to-OpenSSL-libcrypto.patch +Patch57: s390-tools-sles15sp1-0003-zkey-Add-helper-functions-for-comma-separated-string.patch +Patch58: s390-tools-sles15sp1-0004-zkey-Externalize-secure-key-back-end-functions.patch +Patch59: s390-tools-sles15sp1-0005-zkey-Add-keystore-implementation.patch +Patch60: s390-tools-sles15sp1-0006-zkey-Add-keystore-related-commands.patch +Patch61: s390-tools-sles15sp1-0007-zkey-Create-key-repository-and-group-during-make-ins.patch +Patch62: s390-tools-sles15sp1-0008-zkey-Man-page-updates.patch +Patch63: s390-tools-sles15sp1-0009-zkey-let-packaging-create-the-zkeyadm-group-and-perm.patch +Patch64: s390-tools-sles15sp1-0010-zkey-Update-README-to-add-info-about-packaging-requi.patch +Patch65: s390-tools-sles15sp1-0011-zkey-Typo-in-message.patch +Patch66: s390-tools-sles15sp1-0012-zkey-Fix-memory-leak.patch +Patch67: s390-tools-sles15sp1-0013-zkey-Fix-APQN-validation-routine.patch +Patch68: s390-tools-sles15sp1-0014-zkey-Fix-generate-and-import-leaving-key-in-an-incon.patch +Patch69: s390-tools-sles15sp1-0015-zkey-Add-zkey-cryptsetup-tool.patch +Patch70: s390-tools-sles15sp1-0016-zkey-Add-man-page-for-zkey-cryptsetup.patch +Patch71: s390-tools-sles15sp1-0017-zkey-Add-build-dependency-for-libcryptsetup-and-json.patch +Patch72: s390-tools-sles15sp1-0018-zkey-Add-key-verification-pattern-property.patch +Patch73: s390-tools-sles15sp1-0019-zkey-Add-volume-type-property-to-support-LUKS2-volum.patch +Patch74: s390-tools-sles15sp1-01-lszcrypt-CEX6S-exploitation.patch +Patch75: s390-tools-sles15sp1-02-lszcrypt-fix-date-and-wrong-indentation.patch +Patch76: s390-tools-sles15sp1-01-cpumf-Add-extended-counter-defintion-files-for-IBM-z.patch +Patch77: s390-tools-sles15sp1-02-cpumf-z14-split-counter-sets-according-to-CFVN-CSVN-.patch +Patch78: s390-tools-sles15sp1-03-cpumf-cpumf_helper-read-split-counter-sets-part-2-2.patch +Patch79: s390-tools-sles15sp1-04-cpumf-correct-z14-counter-number.patch +Patch80: s390-tools-sles15sp1-05-cpumf-add-missing-Description-tag-for-z13-z14-ctr-12.patch +Patch81: s390-tools-sles15sp1-06-cpumf-correct-counter-name-for-z13-and-z14.patch +Patch82: s390-tools-sles15sp1-07-cpumf-Add-IBM-z14-ZR1-to-the-CPU-Measurement-Facilit.patch +Patch83: s390-tools-sles15sp1-dbginfo-gather-nvme-related-data.patch Patch999: customize-zdev-root-update-script.patch @@ -199,7 +239,7 @@ represented as a file in that directory. %package hmcdrvfs Summary: HMC drive file system based on FUSE License: GPL-2.0 -Group: System Environment/Base +Group: System/Base Requires: fuse %description hmcdrvfs @@ -254,6 +294,45 @@ to list files and directories. %patch42 -p1 %patch43 -p1 %patch44 -p1 +%patch45 -p1 +%patch46 -p1 +%patch47 -p1 +%patch48 -p1 +%patch49 -p1 +%patch50 -p1 +%patch51 -p1 +%patch52 -p1 +%patch53 -p1 +%patch54 -p1 +%patch55 -p1 +%patch56 -p1 +%patch57 -p1 +%patch58 -p1 +%patch59 -p1 +%patch60 -p1 +%patch61 -p1 +%patch62 -p1 +%patch63 -p1 +%patch64 -p1 +%patch65 -p1 +%patch66 -p1 +%patch67 -p1 +%patch68 -p1 +%patch69 -p1 +%patch70 -p1 +%patch71 -p1 +%patch72 -p1 +%patch73 -p1 +%patch74 -p1 +%patch75 -p1 +%patch76 -p1 +%patch77 -p1 +%patch78 -p1 +%patch79 -p1 +%patch80 -p1 +%patch81 -p1 +%patch82 -p1 +%patch83 -p1 %patch999 -p1 @@ -267,12 +346,12 @@ cp -vi %{S:22} CAUTION export OPT_FLAGS="%{optflags}" export KERNELIMAGE_MAKEFLAGS="%%{?_smp_mflags}" -make ZFCPDUMP_DIR=/usr/lib/s390-tools/zfcpdump DISTRELEASE=%{release} +make ZFCPDUMP_DIR=/usr/lib/s390-tools/zfcpdump DISTRELEASE=%{release} HAVE_CRYPTSETUP2=0 gcc -static -o read_values ${OPT_FLAGS} %{S:86} -lqc %install mkdir -p %{buildroot}/boot/zipl -%make_install \ +%make_install HAVE_CRYPTSETUP2=0 \ ZFCPDUMP_DIR=/usr/lib/s390-tools/zfcpdump \ DISTRELEASE=%{release} \ SYSTEMDSYSTEMUNITDIR=%{_unitdir} \ @@ -396,14 +475,18 @@ chmod 755 osasnmpd %pre # check for ts-shell group or create it getent group ts-shell >/dev/null 2>&1 || groupadd -r ts-shell +%service_add_pre appldata.service %service_add_pre cio_ignore.service %service_add_pre cpacfstatsd.service %service_add_pre cpi.service %service_add_pre cpuplugd.service %service_add_pre dumpconf.service +%service_add_pre hsnc.service %service_add_pre mon_fsstatd.service %service_add_pre mon_procd.service %service_add_pre virtsetup.service +%service_add_pre vmlogrdr.service +%service_add_pre xpram.service %post read INITPGM < /proc/1/comm @@ -415,14 +498,18 @@ fi %set_permissions /var/log/ts-shell # Create symbolic links to the scripts from setup and boot directories +%service_add_post appldata.service %service_add_post cio_ignore.service %service_add_post cpacfstatsd.service %service_add_post cpi.service %service_add_post cpuplugd.service %service_add_post dumpconf.service +%service_add_post hsnc.service %service_add_post mon_fsstatd.service %service_add_post mon_procd.service %service_add_post virtsetup.service +%service_add_post vmlogrdr.service +%service_add_post xpram.service # Create the initial versions of the sysconfig files: %{fillup_only -n appldata} @@ -443,33 +530,36 @@ grep -q '^/usr/bin/ts-shell$' /etc/shells \ %{fillup_only -n osasnmpd} %preun -%{stop_on_removal appldata} -%{stop_on_removal hsnc} -%{stop_on_removal vmlogrdr} -%{stop_on_removal xpram} +%service_del_preun appldata.service %service_del_preun cio_ignore.service %service_del_preun cpacfstatsd.service %service_del_preun cpi.service %service_del_preun cpuplugd.service %service_del_preun dumpconf.service +%service_del_preun hsnc.service %service_del_preun mon_fsstatd.service %service_del_preun mon_procd.service %service_del_preun virtsetup.service +%service_del_preun vmlogrdr.service +%service_del_preun xpram.service %postun -%{restart_on_update appldata} -%{restart_on_update hsnc} -%{restart_on_update vmlogrdr} -%{restart_on_update xpram} +%service_del_postun appldata.service %service_del_postun cio_ignore.service %service_del_postun cpacfstatsd.service %service_del_postun cpi.service %service_del_postun cpuplugd.service %service_del_postun dumpconf.service +%service_del_postun hsnc.service %service_del_postun mon_fsstatd.service %service_del_postun mon_procd.service %service_del_postun virtsetup.service +%service_del_postun vmlogrdr.service +%service_del_postun xpram.service +# Even though SLES15+ is systemd based, the build service doesn't +# run it, so we have to make sure we can safely issue the +# systemctl command. read INITPGM < /proc/1/comm if [ "${INITPGM}" == "systemd" ]; then echo "Running systemctl daemon-reload." @@ -480,7 +570,7 @@ if [ ! -x /boot/zipl ]; then echo "Attention, after uninstalling this package," echo "you will NOT be able to IPL from DASD anymore!!!" fi -%{insserv_cleanup} + if test x$1 = x0; then # remove ts-shell from /etc/shells grep -v '^/usr/bin/ts-shell$' /etc/shells > /etc/shells.ts-new