File s390-tools-sles15sp2-24-zkey-Add-helper-functions-to-build-lists-of-APQNs.patch of Package s390-tools.19914

Subject: zkey: Add helper functions to build lists of APQNs
From: Ingo Franzki <ifranzki@linux.ibm.com>

Summary:     zkey: Add support for CCA AES CIPHER keys
Description: With CCA 5 there is a new secure key type, the so called 
             variable length symmetric cipher key token. This token format
             can hold AES keys with size 128, 192 and 256 bits together
             with additional attributes cryptographic bound to the key
             token. The attributes may limit the usage of the key, for
             example restrict export or usability scope. So this key type
             is considered to be even more secure than the traditional 
             secure key token. This key token type is also called "CCA
             AES CIPHER key", where the formerly used key token is called
             "CCA AES DATA key".
             The zkey as well as the zkey-cryptsetup tools are enhanced
             to support AES CIPHER keys. That is, zkey can manage AES DATA 
             keys, as well as AES CIPHER keys. The key type must be specified
             at key generation time, the default is to generate AED DATA
             keys.
Upstream-ID: 663d362ff3b1036476bfce9e2563272bab087013
Problem-ID:  SEC1717

Upstream-Description:

             zkey: Add helper functions to build lists of APQNs

             The new IOCTLs are based on list of APQNs that they try to send
             the request to. Add some helper functions to build such lists
             of APQNs based on the key type, and optionally a given mkvp.

             Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
             Reviewed-by: Harald Freudenberger <freude@linux.ibm.com>
             Signed-off-by: Jan Hoeppner <hoeppner@linux.ibm.com>


Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
---
 zkey/pkey.c |  383 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 383 insertions(+)

--- a/zkey/pkey.c
+++ b/zkey/pkey.c
@@ -46,6 +46,8 @@
 
 #define DEFAULT_KEYBITS		256
 
+#define INITIAL_APQN_ENTRIES	16
+
 /**
  * Opens the pkey device and returns its file descriptor.
  *
@@ -498,6 +500,387 @@ static int pkey_verifyseck2(int pkey_fd,
 }
 
 /**
+ * Print a list of APQNs if verbose is set
+ */
+static void pr_verbose_apqn_list(bool verbose, struct pkey_apqn *list, u32 num)
+{
+	u32 i;
+
+	if (!verbose)
+		return;
+
+	for (i = 0; i < num ; i++)
+		warnx("  APQN: %02x.%04x", list[i].card, list[i].domain);
+}
+
+/**
+ * Filter a n array list of APQNs (struct pkey_apqn) by a list of APQN strings.
+ *
+ * @param[in] apqn_list     a zero terminated array of pointers to C-strings
+ * @param[in/out] apqns     A list of APQNs as array of struct pkey_apqn to
+ *                          filter. The list is modified during filtering.
+ * @param[in/out] apqn_entries Number of entries in the list of APQNs. The
+ *                          number is modified during filtering.
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int filter_apqn_list(const char **apqn_list, struct pkey_apqn **apqns,
+			    u32 *apqn_entries)
+{
+	unsigned int count, i, k, card, domain;
+	struct pkey_apqn *list = *apqns;
+	bool found;
+
+	if (apqn_list == NULL)
+		return 0;
+
+	for (count = 0; apqn_list[count] != NULL; count++)
+		;
+	if (count == 0)
+		return 0;
+
+	for (i = 0; i < *apqn_entries; i++) {
+		found = false;
+		for (k = 0; apqn_list[k] != NULL; k++) {
+			if (sscanf(apqn_list[k], "%x.%x", &card, &domain) != 2)
+				return -EINVAL;
+
+			if (list[i].card == card && list[i].domain == domain) {
+				found = true;
+				break;
+			}
+		}
+
+		if (!found) {
+			if (i < *apqn_entries - 1)
+				memmove(&list[i], &list[i+1],
+					(*apqn_entries - i - 1) *
+						sizeof(struct pkey_apqn));
+			(*apqn_entries)--;
+			i--;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Build a list of APQNs in the form accepted by the pkey IOCTLs from the
+ * List of APQNs as zero terminated array of pointers to C-strings that
+ * are usable for the CCA-AESDATA key type.
+ *
+ * @param[in] apqn_list     a zero terminated array of pointers to C-strings
+ * @param[out] apqns        A list of APQNs as array of struct pkey_apqn. The
+ *                          list must be freed by the caller using free().
+ * @param[out] apqn_entries Number of entries in the list of APQNs
+ * @param[in] verbose       if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int build_apqn_list_for_aes_data(const char **apqn_list,
+					struct pkey_apqn **apqns,
+					u32 *apqn_entries, bool verbose)
+{
+	unsigned int card, domain, count = 0;
+	struct pkey_apqn *list = NULL;
+	u32 list_entries = 0;
+	int i;
+
+	pr_verbose(verbose, "Build a list of APQNs for CCA-AESDATA");
+
+	if (apqn_list != NULL)
+		for (count = 0; apqn_list[count] != NULL; count++)
+			;
+
+	if (count > 0) {
+		list = util_malloc(count * sizeof(struct pkey_apqn));
+		list_entries = count;
+
+		for (i = 0; apqn_list[i] != NULL; i++) {
+			if (sscanf(apqn_list[i], "%x.%x", &card, &domain) != 2)
+				return -EINVAL;
+
+			list[i].card = card;
+			list[i].domain = domain;
+		}
+
+	} else {
+		/*
+		 * Although the new pkey IOCTLs do not support APQN entries
+		 * with ANY indication, build an ANY-list here. If we get here,
+		 * then the new IOCTLs are not available, and it will fall back
+		 * to the old IOCTL which do support ANY specifications.
+		 */
+		list = util_malloc(sizeof(struct pkey_apqn));
+		list_entries = 1;
+
+		list[0].card = AUTOSELECT;
+		list[0].domain = AUTOSELECT;
+	}
+
+	*apqns = list;
+	*apqn_entries = list_entries;
+
+	pr_verbose(verbose, "%u APQNs found", list_entries);
+	pr_verbose_apqn_list(verbose, list, list_entries);
+	return 0;
+}
+
+/**
+ * Build a list of APQNs in the form accepted by the pkey IOCTLs from the
+ * List of APQNs as zero terminated array of pointers to C-strings that
+ * are usable for the specified key type.
+ *
+ * @param[in] pkey_fd       the pkey file descriptor
+ * @param[in] type          the key type
+ * @param[in] apqn_list     a zero terminated array of pointers to C-strings
+ * @param[out] apqns        A list of APQNs as array of struct pkey_apqn. The
+ *                          list must be freed by the caller using free().
+ * @param[out] apqn_entries Number of entries in the list of APQNs
+ * @param[in] verbose       if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int build_apqn_list_for_key_type(int pkey_fd, enum pkey_key_type type,
+					const char **apqn_list,
+					struct pkey_apqn **apqns,
+					u32 *apqn_entries, bool verbose)
+{
+	struct pkey_apqns4keytype apqns4keytype;
+	int rc;
+
+	util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
+	util_assert(apqns != NULL, "Internal error: apqns is NULL");
+	util_assert(apqn_entries != NULL,
+		    "Internal error: apqn_entries is NULL");
+
+	pr_verbose(verbose, "Build a list of APQNs for key type %d", type);
+
+	memset(&apqns4keytype, 0, sizeof(apqns4keytype));
+	apqns4keytype.type = type;
+	apqns4keytype.apqn_entries = INITIAL_APQN_ENTRIES;
+	apqns4keytype.apqns = (struct pkey_apqn *)util_malloc(
+			apqns4keytype.apqn_entries * sizeof(struct pkey_apqn));
+
+	do {
+		rc = ioctl(pkey_fd, PKEY_APQNS4KT, &apqns4keytype);
+		if (rc == 0)
+			break;
+		rc = -errno;
+		pr_verbose(verbose, "ioctl PKEY_APQNS4KT rc: %s",
+			   strerror(-rc));
+
+		switch (rc) {
+		case -ENOSPC:
+			free(apqns4keytype.apqns);
+			apqns4keytype.apqns = (struct pkey_apqn *)
+				util_malloc(apqns4keytype.apqn_entries *
+						sizeof(struct pkey_apqn));
+			continue;
+		case -ENOTTY:
+			/*
+			 * New IOCTL is not available: build the list
+			 * manually (Key type CCA-AESDATA only)
+			 */
+			free(apqns4keytype.apqns);
+
+			if (type != PKEY_TYPE_CCA_DATA)
+				return -ENOTSUP;
+
+			rc = build_apqn_list_for_aes_data(apqn_list, apqns,
+							  apqn_entries,
+							  verbose);
+			return rc;
+		default:
+			goto out;
+		}
+	} while (rc != 0);
+
+	if (apqns4keytype.apqn_entries == 0) {
+		pr_verbose(verbose, "No APQN available for key type %d", type);
+		rc = -ENODEV;
+		goto out;
+	}
+
+	rc = filter_apqn_list(apqn_list, &apqns4keytype.apqns,
+			      &apqns4keytype.apqn_entries);
+	if (rc != 0)
+		goto out;
+
+	if (apqns4keytype.apqn_entries == 0) {
+		pr_verbose(verbose, "No APQN available for key type %d", type);
+		rc = -ENODEV;
+		goto out;
+	}
+
+	pr_verbose(verbose, "%u APQNs found", apqns4keytype.apqn_entries);
+	pr_verbose_apqn_list(verbose, apqns4keytype.apqns,
+			     apqns4keytype.apqn_entries);
+
+out:
+	if (rc == 0) {
+		*apqns = apqns4keytype.apqns;
+		*apqn_entries = apqns4keytype.apqn_entries;
+	} else {
+		*apqns = NULL;
+		*apqn_entries = 0;
+		free(apqns4keytype.apqns);
+	}
+
+	return rc;
+}
+
+/**
+ * Build a list of APQNs in the form accepted by the pkey IOCTLs from the
+ * List of APQNs as zero terminated array of pointers to C-strings that are
+ * usable for the specufied key.
+ *
+ * @param[in] pkey_fd       the pkey file descriptor
+ * @param[in] key           the key
+ * @param[in] keylen        the length of the key
+ * @param[in] flags         PKEY_FLAGS_MATCH_xxx flags
+ * @param[in] apqn_list     a zero terminated array of pointers to C-strings
+ * @param[out] apqns        A list of APQNs as array of struct pkey_apqn. The
+ *                          list must be freed by the caller using free().
+ * @param[out] apqn_entries Number of  entries in the list of APQNs
+ * @param[in] verbose       if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int build_apqn_list_for_key(int pkey_fd, u8 *key, u32 keylen, u32 flags,
+				   const char **apqn_list,
+				   struct pkey_apqn **apqns,
+				   u32 *apqn_entries, bool verbose)
+{
+	struct pkey_apqns4key apqns4key;
+	u64 mkvp;
+	int rc;
+
+	util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
+	util_assert(key != NULL, "Internal error: key is NULL");
+	util_assert(apqns != NULL, "Internal error: apqns is NULL");
+	util_assert(apqn_entries != NULL,
+		    "Internal error: apqn_entries is NULL");
+
+	pr_verbose(verbose, "Build a list of APQNs for the key");
+
+	memset(&apqns4key, 0, sizeof(apqns4key));
+	apqns4key.key = key;
+	apqns4key.keylen = keylen;
+	apqns4key.flags = flags;
+	apqns4key.apqn_entries = INITIAL_APQN_ENTRIES;
+	apqns4key.apqns = (struct pkey_apqn *)util_malloc(
+			apqns4key.apqn_entries * sizeof(struct pkey_apqn));
+
+	do {
+		rc = ioctl(pkey_fd, PKEY_APQNS4K, &apqns4key);
+		if (rc == 0)
+			break;
+		rc = -errno;
+		pr_verbose(verbose, "ioctl PKEY_APQNS4K rc: %s", strerror(-rc));
+
+		switch (rc) {
+		case -ENOSPC:
+			free(apqns4key.apqns);
+			apqns4key.apqns = (struct pkey_apqn *)
+				util_malloc(apqns4key.apqn_entries *
+					sizeof(struct pkey_apqn));
+			continue;
+		case -ENOTTY:
+			/*
+			 * New IOCTL is not available: build the list manually
+			 * (Key type CCA-AESDATA only)
+			 */
+			free(apqns4key.apqns);
+
+			if (!is_cca_aes_data_key(key, keylen))
+				return -ENOTSUP;
+
+			rc = get_master_key_verification_pattern(key, keylen,
+								 &mkvp,
+								 verbose);
+			if (rc != 0)
+				return rc;
+
+			rc = build_apqn_list_for_aes_data(apqn_list, apqns,
+							  apqn_entries,
+							  verbose);
+			return rc;
+		default:
+			goto out;
+		}
+	} while (rc != 0);
+
+	if (apqns4key.apqn_entries == 0) {
+		pr_verbose(verbose, "No APQN available for the key");
+		rc = -ENODEV;
+		goto out;
+	}
+
+	rc = filter_apqn_list(apqn_list, &apqns4key.apqns,
+			      &apqns4key.apqn_entries);
+	if (rc != 0)
+		goto out;
+
+	if (apqns4key.apqn_entries == 0) {
+		pr_verbose(verbose, "No APQN available for the key");
+		rc = -ENODEV;
+		goto out;
+	}
+
+	pr_verbose(verbose, "%u APQNs found", apqns4key.apqn_entries);
+	pr_verbose_apqn_list(verbose, apqns4key.apqns, apqns4key.apqn_entries);
+
+out:
+	if (rc == 0) {
+		*apqns = apqns4key.apqns;
+		*apqn_entries = apqns4key.apqn_entries;
+	} else {
+		*apqns = NULL;
+		*apqn_entries = 0;
+		free(apqns4key.apqns);
+	}
+
+	return rc;
+}
+
+/**
+ * Convert the key type string into the pkey enumeration
+ *
+ * @param[in] key_type      the type of the key
+ *
+ * @returns the pkey key type or 0 for an u known key type
+ */
+static enum pkey_key_type key_type_to_pkey_type(const char *key_type)
+{
+	if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
+		return PKEY_TYPE_CCA_DATA;
+	if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
+		return PKEY_TYPE_CCA_CIPHER;
+
+	return 0;
+}
+
+/**
+ * Return the size of a key blob for a specific type
+ *
+ * @param[in] type          the type of the key
+ *
+ * @returns the size of the key or 0 for an invalid key type
+ */
+static size_t key_size_for_type(enum pkey_key_type type)
+{
+	switch (type) {
+	case PKEY_TYPE_CCA_DATA:
+		return AESDATA_KEY_SIZE;
+	case PKEY_TYPE_CCA_CIPHER:
+		return AESCIPHER_KEY_SIZE;
+	default:
+		return 0;
+	}
+}
+
+/**
  * Generate a secure key by random
  *
  * @param[in] pkey_fd       the pkey file descriptor
openSUSE Build Service is sponsored by