File s390-tools-sles15sp1-01-zcryptctl-new-tool-zcryptctl-for-multiple-zcrypt-node.patch of Package s390-tools.12120

Subject: zcryptctl: new tool zcryptctl for multiple zcrypt node management
From: Harald Freudenberger <freude@linux.ibm.com>

Summary:     zcryptctl: new tool zcryptctl for multiple zcrypt node
Description: There is a new zcrypt kernel feature which provides
             multiple customizable device nodes for the zcrypt
             device driver. Here is the userspace part of this
             which adds a new application zcryptctl for user
             friendly management of this feature.
Upstream-ID: f05f7d656b13c3904f0c55e86ebe9e9b19fcd222
Problem-ID:  SEC1710

Upstream-Description:

             zcryptctl: new tool zcryptctl for multiple zcrypt node management

             With the latest kernel comes an extension for the zcrypt
             device driver to support multiple zcrypt device nodes.

             Here is the userspace part of this which adds a new
             application zcryptctl for user friendly management of
             this feature.

             Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
             Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>


Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
---
 zconf/zcrypt/Makefile    |    7 
 zconf/zcrypt/zcryptctl.8 |  147 ++++++
 zconf/zcrypt/zcryptctl.c | 1030 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1182 insertions(+), 2 deletions(-)

--- a/zconf/zcrypt/Makefile
+++ b/zconf/zcrypt/Makefile
@@ -1,21 +1,24 @@
 include ../../common.mak
 
-all: chzcrypt lszcrypt
+all: chzcrypt lszcrypt zcryptctl
 
 libs = $(rootdir)/libutil/libutil.a
 
 chzcrypt: chzcrypt.o misc.o $(libs)
 lszcrypt: lszcrypt.o misc.o $(libs)
+zcryptctl:  zcryptctl.o misc.o $(libs)
 
 install: all
 	$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
 	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 chzcrypt $(DESTDIR)$(BINDIR)
 	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lszcrypt $(DESTDIR)$(BINDIR)
+	$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 zcryptctl $(DESTDIR)$(BINDIR)
 	$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8
 	$(INSTALL) -m 644 -c chzcrypt.8 $(DESTDIR)$(MANDIR)/man8
 	$(INSTALL) -m 644 -c lszcrypt.8 $(DESTDIR)$(MANDIR)/man8
+	$(INSTALL) -m 644 -c zcryptctl.8 $(DESTDIR)$(MANDIR)/man8
 
 clean:
-	rm -f *.o chzcrypt lszcrypt
+	rm -f *.o chzcrypt lszcrypt zcryptctl
 
 .PHONY: all install clean
--- /dev/null
+++ b/zconf/zcrypt/zcryptctl.8
@@ -0,0 +1,147 @@
+.\" zcryptctl.8
+.\"
+.\" Copyright 2018 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.
+.\"
+.\" use
+.\"   groff -man -Tutf8 zcryptctl.8
+.\" or
+.\"   nroff -man zcryptctl.8
+.\" to process this source
+.\"
+.TH ZCRYPTCTL 8 "AUG 2018" "s390-tools"
+.SH NAME
+zcryptctl \- display information and administrate zcrypt multiple device nodes
+.SH SYNOPSIS
+.TP 8
+.B zcryptctl list
+.TP
+.B zcryptctl create
+.R [
+.I node-name
+.R ]
+.TP
+.B zcryptctl destroy
+.I node-name
+.TP
+.B zcryptctl addap
+.R |
+.B delap
+.I node-name adapter-nr
+.TP
+.B zcryptctl adddom
+.R |
+.B deldom
+.I node-name domain-nr
+.TP
+.B zcryptctl addioctl
+.R |
+.B delioctl
+.I node-name ioctl-term
+.TP
+.B zcryptctl config
+.I config-file
+.TP
+.B zcryptctl listconfig
+.SH DESCRIPTION
+The
+.B zcryptctl
+command displays information and maintains the multi device node
+extension for the zcrypt device driver.
+.P
+With the multi device node extension you can create and configure
+additional zcrypt device nodes which can be used as alternate device
+nodes to access the crypto hardware provided by the zcrypt device
+driver. Each zcrypt device node can be restricted in terms of crypto
+cards, domains, and available ioctls. Such a device node can be used
+as a base for container solutions like Docker to control and restrict
+the access to crypto resources.
+.SH COMMANDS
+.TP 8
+.B zcryptctl list
+Show all the additional device nodes that are currently active.
+.TP
+.B zcryptctl create
+.R [
+.I node-name
+.R ]
+Create a new zcrypt device node. The \fInode-name\fP might be given
+and needs to be unique and not in use. If there is no node name
+provided, the zcrypt device driver will create a new one with pattern
+zcrypt_\fIx\fP, with \fIx\fP being the next free number. Up to 256
+additional device nodes can be created. The newly created additional
+device node appears in /dev and has read and write permissions enabled
+only for root. By default all adapters, domains and ioctls are
+initially disabled on this new device node.
+.TP
+.B zcryptctl destroy
+.I node-name
+Destroy an additional zcrypt device node. The device node is only
+marked for disposal and destroyed when it is no longer used.
+.TP
+.B zcryptctl addap
+.R |
+.B delap
+.I node-name adapter-nr
+Update the filter for the specified zcrypt device node and add or
+delete a crypto adapter to be accessible via this node. The symbol
+\fBALL\fP can be used to enable or disable all adapters.
+.TP
+.B zcryptctl adddom
+.R |
+.B deldom
+.I node-name domain-nr
+Update the filter for the specified zcrypt device node and add or
+delete a domain to be accessible through this node. The symbol
+\fBALL\fP can be used to enable or disable all domains.
+.TP
+.B zcryptctl addioctl
+.R |
+.B delioctl
+.I node-name ioctl-term
+Update the filter for the specified zcrypt device node and add or
+delete an ioctl. The ioctl might be specified as symbolic string (one
+of \fBICARSAMODEXPO\fP, \fBICARSACRT\fP, \fBZSECSENDCPRB\fP,
+\fBZSENDEP11CPRB\fP, \fBZCRYPT_DEVICE_STATUS\fP,
+\fBZCRYPT_STATUS_MASK\fP, \fBZCRYPT_QDEPTH_MASK\fP,
+\fBZCRYPT_PERDEV_REQCNT\fP) or numeric value in the range 0-255 and
+the symbol \fBALL\fP can be used to include all ioctls.
+.TP
+.B zcryptctl config
+.I config-file
+Process a config file. The given configuration file is read line by
+line and the settings are applied. Syntax is simple:
+.RS
+.IP "node=<node-name>"
+.IP "aps=<list of ap numbers separated by space, tab or ','>"
+.IP "doms=<list of domain numbers separated by space, tab or ','>"
+.IP "ioctls=<list of ioctl as numeric or symbolic number separated by space, tab or ','>"
+.LP
+Empty lines are ignored and the '#' marks the rest of the
+line as comment.
+.LP
+The \fBnode=\fP line creates a new zcrypt device node, the \fBaps=\fP,
+\fBdoms=\fP and \fBioctls=\fP lines customize the previously created
+node. The symbol \fBALL\fP is also recognized for aps, doms, and
+ioctls.
+.LP
+Each action must fit into one line, spreading over multiple lines is
+not supported. But you can use more than one \fBaps=\fP, \fBdoms=\fP
+and \fBioctls=\fP lines to customize the very same node.
+.LP
+Processing stops when a line cannot be parsed or the current action
+fails. In this case the exit status is non zero but the successful
+actions until the failure occurs are not rolled back.
+.RE
+.TP
+.B zcryptctl listconfig
+List the current configuration in a form suitable for input to the
+\fBzcryptctl config\fP command.
+.LP
+.SH EXIT STATUS
+On successful completion of the command the exit status is 0. A non
+zero return code (and some kind of failure message) is emitted if the
+processing could not complete successful.
+.SH SEE ALSO
+\fBlszcrypt\fR(8)
--- /dev/null
+++ b/zconf/zcrypt/zcryptctl.c
@@ -0,0 +1,1030 @@
+/*
+ * zcryptctl - Maintain zcrypt multi device nodes.
+ *
+ * by Harald Freudenberger <freude@linux.ibm.com>
+ * 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 <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#include "lib/util_base.h"
+#include "lib/util_file.h"
+#include "lib/util_opt.h"
+#include "lib/util_panic.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 "lib/zt_common.h"
+
+#define MAX_ZDEV_IOCTLS 256
+#define ZCRYPT_NAME  "zcrypt"
+#define MAX_ZDEV_CARDIDS_EXT 256
+#define MAX_ZDEV_DOMAINS_EXT 256
+#define ZCRYPTDEVICE "/dev/z90crypt"
+#define _UNUSED_ __attribute__((unused))
+
+/*
+ * Currently known commands
+ */
+#define CMD_LIST       0x0001
+#define CMD_CREATE     0x0002
+#define CMD_DESTROY    0x0003
+#define CMD_ADD_AP     0x0004
+#define CMD_DEL_AP     0x0005
+#define CMD_ADD_DOM    0x0006
+#define CMD_DEL_DOM    0x0007
+#define CMD_ADD_IOCTL  0x0008
+#define CMD_DEL_IOCTL  0x0009
+#define CMD_CONFIG     0x000A
+#define CMD_LISTCONFIG 0x000B
+
+/*
+ * Program configuration
+ */
+static const struct util_prg prg = {
+	.args = "",
+	.command_args = "COMMAND [COMMAND-PARAMS]",
+	.desc = "Display and administrate zcrypt multiple device nodes.",
+	.copyright_vec = {
+		{
+			.owner = "IBM Corp.",
+			.pub_first = 2018,
+			.pub_last = 2018,
+		},
+		UTIL_PRG_COPYRIGHT_END
+	}
+};
+
+static struct util_opt opt_vec[] = {
+	UTIL_OPT_HELP,
+	UTIL_OPT_VERSION,
+	UTIL_OPT_END
+};
+
+/*
+ * List of currently known and supported ioctls
+ */
+static struct zcryptctl_ioctls_s {
+	int nr;
+	const char *name;
+} zcryptctl_ioctls[] = {
+	{
+		.name = "ICARSAMODEXPO",
+		.nr = 0x05,
+	},
+	{
+		.name = "ICARSACRT",
+		.nr = 0x06,
+	},
+	{
+		.name = "ZSECSENDCPRB",
+		.nr = 0x81,
+	},
+	{
+		.name = "ZSENDEP11CPRB",
+		.nr = 0x04,
+	},
+	{
+		.name = "ZCRYPT_DEVICE_STATUS",
+		.nr = 0x5f,
+	},
+	{
+		.name = "ZCRYPT_STATUS_MASK",
+		.nr = 0x58,
+	},
+	{
+		.name = "ZCRYPT_QDEPTH_MASK",
+		.nr = 0x59,
+	},
+	{
+		.name = "ZCRYPT_PERDEV_REQCNT",
+		.nr = 0x5a,
+	},
+	{
+		.name = NULL,
+		.nr = 0,
+	},
+};
+
+static int ioctlstr2value(const char *str)
+{
+	int i;
+
+	for (i = 0; zcryptctl_ioctls[i].name; i++)
+		if (strcasecmp(str, zcryptctl_ioctls[i].name) == 0)
+			return zcryptctl_ioctls[i].nr;
+
+	return -1;
+}
+
+static const char *value2ioctlstr(int value)
+{
+	int i;
+
+	for (i = 0; zcryptctl_ioctls[i].name; i++)
+		if (value == zcryptctl_ioctls[i].nr)
+			return zcryptctl_ioctls[i].name;
+
+	return NULL;
+}
+
+static int check_nodename(const char *nodename)
+{
+	struct stat sb;
+	const char *node;
+	char pathname[PATH_MAX];
+
+	node = strrchr(nodename, '/');
+	node = node ? node + 1 : nodename;
+	snprintf(pathname, sizeof(pathname), "/dev/%s", node);
+	pathname[sizeof(pathname) - 1] = '\0';
+	if (stat(pathname, &sb) != 0)
+		return -1;
+	if (!S_ISCHR(sb.st_mode))
+		return -2;
+
+	return 0;
+}
+
+static int check_zcrypt_class_dir(void)
+{
+	int rc = 0;
+	char *afile;
+
+	afile = util_path_sysfs("class/%s", ZCRYPT_NAME);
+	if (!util_path_is_dir(afile))
+		rc = -1;
+
+	free(afile);
+	return rc;
+}
+
+static int fetch_major_minor(const char *nodename, int *major, int *minor)
+{
+	FILE *f;
+	int rc = 0;
+	char *afile;
+	const char *node;
+
+	node = strrchr(nodename, '/');
+	node = node ? node + 1 : nodename;
+	afile = util_path_sysfs("class/%s/%s/dev", ZCRYPT_NAME, node);
+	f = fopen(afile, "r");
+	if (!f) {
+		rc = -1;
+		goto out;
+	}
+	if (fscanf(f, "%i:%i", major, minor) != 2) {
+		fclose(f);
+		rc = -2;
+		goto out;
+	}
+	fclose(f);
+
+out:
+	free(afile);
+	return rc;
+}
+
+static int write_dn_attr(const char *nodename, const char *attr,
+			 const char *value)
+{
+	FILE *f;
+	int rc = 0;
+	char *afile;
+	const char *node;
+
+	if (nodename) {
+		node = strrchr(nodename, '/');
+		node = node ? node + 1 : nodename;
+		afile = util_path_sysfs("class/%s/%s/%s",
+					ZCRYPT_NAME, node, attr);
+	} else
+		afile = util_path_sysfs("class/%s/%s", ZCRYPT_NAME, attr);
+	f = fopen(afile, "w");
+	if (!f) {
+		rc = -1;
+		goto out;
+	}
+	if (fprintf(f, "%s\n", value) < 0) {
+		fclose(f);
+		rc = -2;
+		goto out;
+	}
+	fflush(f);
+	if (ferror(f)) {
+		fclose(f);
+		rc = -2;
+		goto out;
+	}
+
+	fclose(f);
+
+out:
+	free(afile);
+	return rc;
+}
+
+static int read_dn_attr(const char *nodename, const char *attr,
+			char *value, int valuelen)
+{
+	int rc;
+	FILE *f;
+	char *afile;
+	const char *node;
+
+	node = strrchr(nodename, '/');
+	node = node ? node + 1 : nodename;
+	afile = util_path_sysfs("class/%s/%s/%s", ZCRYPT_NAME, node, attr);
+	f = fopen(afile, "r");
+	if (!f) {
+		rc = -1;
+		goto out;
+	}
+	value = fgets(value, valuelen, f);
+	fclose(f);
+	rc = value ? 0 : -2;
+
+out:
+	free(afile);
+	return rc;
+}
+
+static int test_bit(int n, const char *hexbytestr)
+{
+	char c;
+	int v, i = 0;
+
+	if (strncmp(hexbytestr, "0x", 2) == 0)
+		i += 2;
+	c = hexbytestr[i + n / 4];
+	if (c >= '0' && c <= '9')
+		v = c - '0';
+	else if (c >= 'a' && c <= 'f')
+		v = 10 + c - 'a';
+	else if (c >= 'A' && c <= 'F')
+		v = 10 + c - 'A';
+	else
+		errx(EXIT_FAILURE,
+		     "Could not parse hex digit '%c'", c);
+
+	return v & (1 << (3 - (n % 4)));
+}
+
+static int cmd_list(int cmd,
+		    const char *node _UNUSED_,
+		    const char *arg _UNUSED_)
+{
+	DIR *dir;
+	char *dirname;
+	const char *p;
+	struct dirent *de;
+	int i, n, major, minor, count = 0;
+	char buf[80], tab = (cmd == CMD_LISTCONFIG ? ' ' : '\t');
+
+	dirname = util_path_sysfs("class/%s", ZCRYPT_NAME);
+	dir = opendir(dirname);
+	if (!dir)
+		errx(EXIT_FAILURE,
+		     "Could not read directory '%s' errno=%d (%s)",
+		     dirname, errno, strerror(errno));
+	while ((de = readdir(dir)) != NULL) {
+		if (de->d_name[0] == '.' || de->d_type == DT_REG)
+			continue;
+		if (fetch_major_minor(de->d_name, &major, &minor) != 0)
+			errx(EXIT_FAILURE,
+			     "Could not fetch major/minor from sysfs for zcrypt node '%s'",
+			     de->d_name);
+		if (cmd == CMD_LISTCONFIG) {
+			printf("node = %s\n", de->d_name);
+			printf(" aps =");
+		} else {
+			printf("zcrypt node name:\t%s\n", de->d_name);
+			printf("  device node:\t/dev/%s\n", de->d_name);
+			printf("  major:minor:\t%d:%d\n", major, minor);
+			printf("  adapter:");
+		}
+		if (read_dn_attr(de->d_name, "apmask", buf, sizeof(buf)) != 0)
+			errx(EXIT_FAILURE,
+			     "Could not fetch apmask attribute from sysfs for zcrypt node '%s'",
+			     de->d_name);
+		for (i = n = 0; i < MAX_ZDEV_CARDIDS_EXT; i++)
+			if (test_bit(i, buf))
+				printf("%c%d", n++ == 0 ? tab : ',', i);
+		putchar('\n');
+		if (cmd == CMD_LISTCONFIG)
+			printf(" doms =");
+		else
+			printf("  domains:");
+		if (read_dn_attr(de->d_name, "aqmask", buf, sizeof(buf)) != 0)
+			errx(EXIT_FAILURE,
+			     "Could not fetch aqmask attribute from sysfs for zcrypt node '%s'",
+			     de->d_name);
+		for (i = n = 0; i < MAX_ZDEV_DOMAINS_EXT; i++)
+			if (test_bit(i, buf))
+				printf("%c%d", n++ == 0 ? tab : ',', i);
+		putchar('\n');
+		if (cmd == CMD_LISTCONFIG)
+			printf(" ioctls =");
+		else
+			printf("  ioctls:");
+		if (read_dn_attr(de->d_name, "ioctlmask",
+				 buf, sizeof(buf)) != 0)
+			errx(EXIT_FAILURE,
+			     "Could not fetch ioctlmask attribute from sysfs for zcrypt node '%s'",
+			     de->d_name);
+		for (i = n = 0; i < MAX_ZDEV_IOCTLS; i++) {
+			if (test_bit(i, buf)) {
+				p = value2ioctlstr(i);
+				if (p)
+					printf("%c%s",
+					       n++ == 0 ? tab : ',', p);
+				else
+					printf("%c%d",
+					       n++ == 0 ? tab : ',', i);
+			}
+		}
+		putchar('\n');
+		count++;
+	}
+	closedir(dir);
+
+	if (count == 0)
+		printf("No additional zcrypt device nodes defined\n");
+
+	return 0;
+}
+
+static int cmd_create(int cmd _UNUSED_,
+		      const char *nodename,
+		      const char *arg _UNUSED_)
+{
+	int rc;
+	const char *node;
+	char buf[PATH_MAX];
+
+	if (nodename) {
+		node = strrchr(nodename, '/');
+		node = node ? node + 1 : nodename;
+		strncpy(buf, node, sizeof(buf) - 1);
+	} else
+		strncpy(buf, "\n", sizeof(buf) - 1);
+	buf[sizeof(buf) - 1] = 0;
+
+	rc = write_dn_attr(NULL, "create", buf);
+	if (rc != 0)
+		errx(EXIT_FAILURE,
+		     "Could not write into sysfs entry to create zdev node");
+
+	printf("Device node created\n");
+
+	return 0;
+}
+
+static int cmd_destroy(int cmd _UNUSED_,
+		       const char *nodename,
+		       const char *arg _UNUSED_)
+{
+	int rc;
+	struct stat sb;
+	const char *node;
+	char pathname[PATH_MAX];
+
+	node = strrchr(nodename, '/');
+	node = node ? node + 1 : nodename;
+	snprintf(pathname, sizeof(pathname), "/dev/%s", node);
+	pathname[sizeof(pathname) - 1] = '\0';
+	rc = stat(pathname, &sb);
+	if (rc != 0)
+		errx(EXIT_FAILURE,
+		     "Could not check status for '%s'", pathname);
+	if (!S_ISCHR(sb.st_mode))
+		errx(EXIT_FAILURE,
+		     "File '%s' is not a character device node", pathname);
+
+	rc = write_dn_attr(NULL, "destroy", node);
+	if (rc != 0)
+		errx(EXIT_FAILURE,
+		     "Could not write into sysfs entry to destroy zdev node '%s'",
+		     node);
+
+	printf("Device node '%s' marked for destruction\n", node);
+
+	return 0;
+}
+
+static void add_del_ap(int cmd, const char *node, int ap)
+{
+	int rc;
+	char buf[PATH_MAX];
+
+	if (cmd == CMD_ADD_AP)
+		sprintf(buf, "+%d", ap);
+	else
+		sprintf(buf, "-%d", ap);
+	rc = write_dn_attr(node, "apmask", buf);
+	if (rc != 0)
+		errx(EXIT_FAILURE,
+		     "Could not write into sysfs entry to %s adapter %d for zdev node '%s'",
+		     cmd == CMD_ADD_AP ? "add" : "remove", ap, node);
+}
+
+static int cmd_add_del_ap(int cmd, const char *node, const char *arg)
+{
+	int ap, all = 0;
+
+	if (strcasecmp(arg, "ALL") == 0) {
+		all = 1;
+	} else {
+		if (sscanf(arg, "%i", &ap) != 1)
+			errx(EXIT_FAILURE,
+			     "Invalid adapter argument '%s'", arg);
+		if (ap < 0 || ap >= MAX_ZDEV_CARDIDS_EXT)
+			errx(EXIT_FAILURE,
+			     "Adapter argument '%s' out of range [0..%d]",
+			     arg, MAX_ZDEV_CARDIDS_EXT - 1);
+	}
+
+	if (!all) {
+		add_del_ap(cmd, node, ap);
+		printf("Adapter %d %s\n", ap,
+		       (cmd == CMD_ADD_AP ? "added" : "removed"));
+	} else {
+		for (ap = 0; ap < MAX_ZDEV_CARDIDS_EXT; ap++)
+			add_del_ap(cmd, node, ap);
+		printf("All adapters %s\n",
+		       (cmd == CMD_ADD_AP ? "added" : "removed"));
+	}
+
+	return 0;
+}
+
+static void add_del_dom(int cmd, const char *node, int dom)
+{
+	int rc;
+	char buf[PATH_MAX];
+
+	if (cmd == CMD_ADD_DOM)
+		sprintf(buf, "+%d", dom);
+	else
+		sprintf(buf, "-%d", dom);
+	rc = write_dn_attr(node, "aqmask", buf);
+	if (rc != 0)
+		errx(EXIT_FAILURE,
+		     "Could not write into sysfs entry to %s domain %d for zdev node '%s'",
+		     cmd == CMD_ADD_DOM ? "add" : "remove", dom, node);
+}
+
+static int cmd_add_del_dom(int cmd, const char *node, const char *arg)
+{
+	int dom, all = 0;
+
+	if (strcasecmp(arg, "ALL") == 0) {
+		all = 1;
+	} else {
+		if (sscanf(arg, "%i", &dom) != 1)
+			errx(EXIT_FAILURE,
+			     "Invalid domain argument '%s'", arg);
+		if (dom < 0 || dom >= MAX_ZDEV_DOMAINS_EXT)
+			errx(EXIT_FAILURE,
+			     "Domain argument '%s' out of range [0..%d]",
+			     arg, MAX_ZDEV_DOMAINS_EXT - 1);
+	}
+
+	if (!all) {
+		add_del_dom(cmd, node, dom);
+		printf("Domain %d %s\n", dom,
+		       (cmd == CMD_ADD_DOM ? "added" : "removed"));
+	} else {
+		for (dom = 0; dom < MAX_ZDEV_DOMAINS_EXT; dom++)
+			add_del_dom(cmd, node, dom);
+		printf("All domains %s\n",
+		       (cmd == CMD_ADD_DOM ? "added" : "removed"));
+	}
+
+	return 0;
+}
+
+static void add_del_ioctl(int cmd, const char *node, int ioctlnr)
+{
+	int rc;
+	char buf[PATH_MAX];
+
+	if (cmd == CMD_ADD_IOCTL)
+		sprintf(buf, "+%d", ioctlnr);
+	else
+		sprintf(buf, "-%d", ioctlnr);
+	rc = write_dn_attr(node, "ioctlmask", buf);
+	if (rc != 0)
+		errx(EXIT_FAILURE,
+		     "Could not write into sysfs entry to %s ioctl %d for zdev node '%s'",
+		     cmd == CMD_ADD_IOCTL ? "add" : "remove", ioctlnr, node);
+}
+
+static int cmd_add_del_ioctl(int cmd, const char *node, const char *arg)
+{
+	int ioctlnr, all = 0;
+
+	if (strcasecmp(arg, "ALL") == 0) {
+		all = 1;
+	} else {
+		ioctlnr = ioctlstr2value(arg);
+		if (ioctlnr < 0)
+			if (sscanf(arg, "%i", &ioctlnr) != 1)
+				errx(EXIT_FAILURE,
+				     "Invalid ioctl argument '%s'", arg);
+		if (ioctlnr < 0 || ioctlnr >= MAX_ZDEV_IOCTLS)
+			errx(EXIT_FAILURE,
+			     "Ioctl argument '%s' out of range [0..%d]",
+			     arg, MAX_ZDEV_IOCTLS - 1);
+	}
+
+	if (!all) {
+		add_del_ioctl(cmd, node, ioctlnr);
+		printf("Ioctl %s %s\n", arg,
+		       (cmd == CMD_ADD_IOCTL ? "added" : "removed"));
+	} else {
+		for (ioctlnr = 0; ioctlnr < MAX_ZDEV_IOCTLS; ioctlnr++)
+			add_del_ioctl(cmd, node, ioctlnr);
+		printf("All Ioctls %s\n",
+		       (cmd == CMD_ADD_IOCTL ? "added" : "removed"));
+	}
+
+	return 0;
+}
+
+static int _match_keyword(char **p, const char *keyword)
+{
+	int n = strlen(keyword);
+
+	if (strncmp(*p, keyword, n) == 0) {
+		*p += n;
+		return n;
+	}
+
+	return 0;
+}
+
+static int _match_character(char **p, char c)
+{
+	char *q = *p;
+
+	while (isblank(*q))
+		q++;
+	if (*q != c)
+		return 0;
+	q++;
+	while (isblank(*q))
+		q++;
+	*p = q;
+
+	return 1;
+}
+
+static int _match_string(char **p, char *buf)
+{
+	int n = 0;
+	char *q = *p;
+
+	while (isblank(*q))
+		q++;
+	while (*q && *q != ',' && !isspace(*q)) {
+		buf[n++] = *q;
+		q++;
+	}
+	while (isblank(*q))
+		q++;
+
+	if (n > 0) {
+		buf[n] = '\0';
+		*p = q;
+	}
+
+	return n;
+}
+
+static int cmd_config(int cmd _UNUSED_,
+		      const char *nodename _UNUSED_,
+		      const char *arg)
+{
+	ssize_t n;
+	size_t linesize = 0;
+	int nr = 0, havenode = 0;
+	FILE *f = fopen(arg, "r");
+	char *p, *line = NULL, node[128], buf[128];
+
+	if (!f)
+		errx(EXIT_FAILURE,
+		     "Could not open file '%s'", arg);
+
+	while ((n = getline(&line, &linesize, f)) != -1) {
+		nr++;
+		p = line;
+		while (isspace(*p))
+			p++;
+		if (*p == '\0' || *p == '#')
+			continue;
+		if (_match_keyword(&p, "node")) {
+			if (!_match_character(&p, '='))
+				errx(EXIT_FAILURE,
+				     "Missing '=' at '%-8.8s...' in line %d '%s'",
+				     p, nr, line);
+			if (!_match_string(&p, node))
+				errx(EXIT_FAILURE,
+				     "Missing node name at '%-8.8s...' in line %d '%s'",
+				     p, nr, line);
+			cmd_create(CMD_CREATE, node, NULL);
+			havenode = 1;
+		} else if (_match_keyword(&p, "aps")) {
+			if (!havenode)
+				errx(EXIT_FAILURE,
+				     "Missing node=... before processing any aps=... statements in line %d '%s'",
+				     nr, line);
+			if (!_match_character(&p, '='))
+				errx(EXIT_FAILURE,
+				     "Missing '=' at '%-8.8s...' in line %d '%s'",
+				     p, nr, line);
+			while (1) {
+				while (isspace(*p))
+					p++;
+				if (*p == '\0' || *p == '#')
+					break;
+				if (!_match_string(&p, buf))
+					errx(EXIT_FAILURE,
+					     "Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'",
+					     p, nr, line);
+				cmd_add_del_ap(CMD_ADD_AP, node, buf);
+				while (isblank(*p) || *p == ',')
+					p++;
+			}
+		} else if (_match_keyword(&p, "doms")) {
+			if (!havenode)
+				errx(EXIT_FAILURE,
+				     "Missing node=... before processing any doms=... statements in line %d '%s'",
+				     nr, line);
+			if (!_match_character(&p, '='))
+				errx(EXIT_FAILURE,
+				     "Missing '=' at '%-8.8s...' in line %d '%s'",
+				     p, nr, line);
+			while (1) {
+				while (isspace(*p))
+					p++;
+				if (*p == '\0' || *p == '#')
+					break;
+				if (!_match_string(&p, buf))
+					errx(EXIT_FAILURE,
+					     "Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'",
+					     p, nr, line);
+				cmd_add_del_dom(CMD_ADD_DOM, node, buf);
+				while (isblank(*p) || *p == ',')
+					p++;
+			}
+		} else if (_match_keyword(&p, "ioctls")) {
+			if (!havenode)
+				errx(EXIT_FAILURE,
+				     "Missing node=... before processing any ioctls=... statements in line %d '%s'",
+				     nr, line);
+			if (!_match_character(&p, '='))
+				errx(EXIT_FAILURE,
+				     "Missing '=' at '%-8.8s...' in line %d '%s'",
+				     p, nr, line);
+			while (1) {
+				while (isspace(*p))
+					p++;
+				if (*p == '\0' || *p == '#')
+					break;
+				if (!_match_string(&p, buf))
+					errx(EXIT_FAILURE,
+					     "Missing argument(s) for aps=... at '%-8.8s...' in line %d '%s'",
+					     p, nr, line);
+				cmd_add_del_ioctl(CMD_ADD_IOCTL, node, buf);
+				while (isblank(*p) || *p == ',')
+					p++;
+			}
+		} else
+			errx(EXIT_FAILURE,
+			     "Unknown keyword '%-8.8s...' in line %d '%s'",
+			     p, nr, line);
+	}
+
+	free(line);
+	fclose(f);
+
+	return 0;
+}
+
+static struct zcryptctl_cmds_s {
+	int cmd;
+	const char *usage;
+	const char *command;
+	const char *description;
+	int (*function)(int cmd, const char *node, const char *arg);
+} zcryptctl_cmds[] = {
+	{
+		.cmd = CMD_LIST,
+		.command = "list",
+		.function = cmd_list,
+		.usage = "zcryptctl list",
+		.description =
+		"List all currently known additional zcrypt device nodes.",
+	},
+	{
+		.cmd = CMD_CREATE,
+		.command = "create",
+		.function = cmd_create,
+		.usage = "zcryptctl create [nodename]",
+		.description =
+		"Create a new zcrypt device node.\n"
+		"The node-name might be given and needs to be unique and not\n"
+		"in use. If there is no node name provided, the zcrypt device\n"
+		"driver will create a new one with pattern zcrypt_<x>\n"
+		"with <x> being the next free number. By default all\n"
+		"adapters, domains and ioctls are initially disabled on this\n"
+		"new device node."
+	},
+	{
+		.cmd = CMD_DESTROY,
+		.command = "destroy",
+		.function = cmd_destroy,
+		.usage = "zcryptctl destroy <nodename>",
+		.description =
+		"Destroy an additional zcrypt device node.\n"
+		"Mark the given zcrypt device node as disposable. The removal\n"
+		"will take place when it is no longer used.",
+	},
+	{
+		.cmd = CMD_ADD_AP,
+		.command = "addap",
+		.function = cmd_add_del_ap,
+		.usage = "zcryptctl addap <adapter>",
+		.description =
+		"Update the filter for the specified zcrypt device node and\n"
+		"add an crypto adapter to be accessible via this node. The\n"
+		"adapter argument may be a number in the range 0-255 or the\n"
+		"symbol ALL.",
+	},
+	{
+		.cmd = CMD_DEL_AP,
+		.command = "delap",
+		.function = cmd_add_del_ap,
+		.usage = "zcryptctl delap <adapter>",
+		.description =
+		"Update the filter for the specified zcrypt device node and\n"
+		"remove a crypto adapter from the allowed adapters list. The\n"
+		"adapter argument may be a number in the range 0-255 or the\n"
+		"symbol ALL.",
+	},
+	{
+		.cmd = CMD_ADD_DOM,
+		.command = "adddom",
+		.function = cmd_add_del_dom,
+		.usage = "zcryptctl adddom <domain>",
+		.description =
+		"Update the filter for the specified zcrypt device node and\n"
+		"add a crypto domain to be accessible via this node. The\n"
+		"domain argument may be a number in the range 0-255 or the\n"
+		"symbol ALL.",
+	},
+	{
+		.cmd = CMD_DEL_DOM,
+		.command = "deldom",
+		.function = cmd_add_del_dom,
+		.usage = "zcryptctl deldom <adapter>",
+		.description =
+		"Update the filter for the specified zcrypt device node and\n"
+		"remove a crypto domain from the allowed domains list. The\n"
+		"domain argument may be a number in the range 0-255 or the\n"
+		"symbol ALL.",
+	},
+	{
+		.cmd = CMD_ADD_IOCTL,
+		.command = "addioctl",
+		.function = cmd_add_del_ioctl,
+		.usage = "zcryptctl addioctl <ioctlexp>",
+		.description =
+		"Update the filter for the specified zcrypt device node and\n"
+		"add an ioctl number to be accessible via this node. The\n"
+		"ioctlexp argument may be one of symbols ICARSAMODEXPO,\n"
+		"ICARSACRT, ZSECSENDCPRB, ZSENDEP11CPRB, ZCRYPT_DEVICE_STATUS\n"
+		"ZCRYPT_STATUS_MASK, ZCRYPT_QDEPTH_MASK, ZCRYPT_PERDEV_REQCNT\n"
+		"or a number in the range 0-255 or the symbol ALL.",
+	},
+	{
+		.cmd = CMD_DEL_IOCTL,
+		.command = "delioctl",
+		.function = cmd_add_del_ioctl,
+		.usage = "zcryptctl delioctl <ioctlexp>",
+		.description =
+		"Update the filter for the specified zcrypt device node and\n"
+		"remove an ioctl number from the allowed ioctls list. The\n"
+		"ioctlexp argument may be one of symbols ICARSAMODEXPO,\n"
+		"ICARSACRT, ZSECSENDCPRB, ZSENDEP11CPRB, ZCRYPT_DEVICE_STATUS\n"
+		"ZCRYPT_STATUS_MASK, ZCRYPT_QDEPTH_MASK, ZCRYPT_PERDEV_REQCNT\n"
+		"or a number in the range 0-255 or the symbol ALL.",
+	},
+	{
+		.cmd = CMD_CONFIG,
+		.command = "config",
+		.function = cmd_config,
+		.usage = "zcryptctl config <configfile>",
+		.description =
+		"Process a config file. The given config file is read line by\n"
+		"line and the settings are applied. Syntax is simple:\n"
+		"  node=<node_name>\n"
+		"  aps=<list of ap numbers separated by space, tab or ','>\n"
+		"  doms=<list of domains separated by space, tab or ','>\n"
+		"  ioctls=<list of ioctl as number or symbolic number\n"
+		"          separated by space, tab or ','>\n"
+		"Empty lines are ignored and the '#' marks the rest of the\n"
+		"line as comment.\n"
+		"The node= line creates a new zcrypt device node, the\n"
+		"aps=, doms= and ioctls= lines customize the previously\n"
+		"created node. The symbol ALL is also recognized for aps,\n"
+		"doms, and ioctls.\n"
+		"Each action must fit into one line, spreading over multiple\n"
+		"lines is not supported. But you can use more than one\n"
+		"aps=, doms= and ioctls= lines to customize the very same\n"
+		"node.\n"
+		"Processing stops when a line cannot be parsed or the\n"
+		"current action fails. When the config file has been\n"
+		"processed successful, the zcryptctl return code is 0. A non\n"
+		"zero return code (and some kind of failure message) is\n"
+		"emitted on partial completion.",
+	},
+	{
+		.cmd = CMD_LISTCONFIG,
+		.command = "listconfig",
+		.function = cmd_list,
+		.usage = "zcryptctl listconfig",
+		.description =
+		"List all currently known additional zcrypt device nodes\n"
+		"in a format suitable for the 'config' command.",
+	},
+	{
+		.command = NULL,
+		.cmd = 0,
+	}
+};
+
+static int get_command_index(const char *cmdstr)
+{
+	int i;
+
+	for (i = 0; zcryptctl_cmds[i].command; i++)
+		if (!strcmp(zcryptctl_cmds[i].command, cmdstr))
+			return i;
+
+	return -1;
+}
+
+static void commands_print_help(void)
+{
+	int i;
+
+	for (i = 0; zcryptctl_cmds[i].command; i++)
+		if (zcryptctl_cmds[i].usage)
+			printf(" %s\n", zcryptctl_cmds[i].usage);
+}
+
+int main(int argc, char *argv[])
+{
+	int c, cmdindex = -1;
+	int rc = EXIT_SUCCESS;
+
+	util_prg_init(&prg);
+	util_opt_init(opt_vec, NULL);
+
+	for (c = 1; c < argc; c++) {
+		cmdindex = get_command_index(argv[c]);
+		if (cmdindex >= 0)
+			break;
+	}
+
+	while (1) {
+		c = util_opt_getopt_long(argc, argv);
+		if (c == -1)
+			break;
+		switch (c) {
+		case 'h':
+			if (cmdindex < 0) {
+				util_prg_print_help();
+				commands_print_help();
+				util_opt_print_help();
+			} else {
+				printf("Usage: %s\n",
+				       zcryptctl_cmds[cmdindex].usage);
+				printf("%s\n",
+				       zcryptctl_cmds[cmdindex].description);
+			}
+			return EXIT_SUCCESS;
+		case 'v':
+			util_prg_print_version();
+			return EXIT_SUCCESS;
+		default:
+			util_opt_print_parse_error(c, argv);
+			return EXIT_FAILURE;
+		}
+	}
+
+	if (cmdindex < 0)
+		errx(EXIT_FAILURE, "Missing or invalid command argument");
+
+	if (check_zcrypt_class_dir() != 0)
+		errx(EXIT_FAILURE,
+		     "Directory class/%s is missing in sysfs.\n"
+		     "Multiple zcrypt node support is not available",
+		     ZCRYPT_NAME);
+
+	c = zcryptctl_cmds[cmdindex].cmd;
+	switch (c) {
+	case CMD_LIST:
+	case CMD_LISTCONFIG:
+		rc = zcryptctl_cmds[cmdindex].function(c, NULL, NULL);
+		break;
+	case CMD_CREATE:
+		rc = zcryptctl_cmds[cmdindex].function(c,
+						       optind + 1 < argc ?
+						       argv[optind + 1] : NULL,
+						       NULL);
+		break;
+	case CMD_DESTROY:
+		if (optind + 1 >= argc)
+			errx(EXIT_FAILURE, "Missing node name argument");
+		if (check_nodename(argv[optind + 1]) != 0)
+			errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'",
+			     argv[optind + 1]);
+		rc = zcryptctl_cmds[cmdindex].function(c,
+						       argv[optind + 1], NULL);
+		break;
+	case CMD_ADD_AP:
+	case CMD_DEL_AP:
+		if (optind + 1 >= argc)
+			errx(EXIT_FAILURE, "Missing node name argument");
+		if (optind + 2 >= argc)
+			errx(EXIT_FAILURE, "Missing adapter argument");
+		if (check_nodename(argv[optind + 1]) != 0)
+			errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'",
+			     argv[optind + 1]);
+		rc = zcryptctl_cmds[cmdindex].function(c,
+						       argv[optind + 1],
+						       argv[optind + 2]);
+		break;
+	case CMD_ADD_DOM:
+	case CMD_DEL_DOM:
+		if (optind + 1 >= argc)
+			errx(EXIT_FAILURE, "Missing node name argument");
+		if (optind + 2 >= argc)
+			errx(EXIT_FAILURE, "Missing domain argument");
+		if (check_nodename(argv[optind + 1]) != 0)
+			errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'",
+			     argv[optind + 1]);
+		rc = zcryptctl_cmds[cmdindex].function(c,
+						       argv[optind + 1],
+						       argv[optind + 2]);
+		break;
+	case CMD_ADD_IOCTL:
+	case CMD_DEL_IOCTL:
+		if (optind + 1 >= argc)
+			errx(EXIT_FAILURE, "Missing node name argument");
+		if (optind + 2 >= argc)
+			errx(EXIT_FAILURE, "Missing ioctl argument");
+		if (check_nodename(argv[optind + 1]) != 0)
+			errx(EXIT_FAILURE, "Invalid or unknown nodename '%s'",
+			     argv[optind + 1]);
+		rc = zcryptctl_cmds[cmdindex].function(c,
+						       argv[optind + 1],
+						       argv[optind + 2]);
+		break;
+	case CMD_CONFIG:
+		if (optind + 1 >= argc)
+			errx(EXIT_FAILURE, "Missing filename argument");
+		rc = zcryptctl_cmds[cmdindex].function(c, NULL,
+						       argv[optind + 1]);
+		break;
+	default:
+		errx(EXIT_FAILURE, "Unknown command %d", c);
+	}
+
+	return rc;
+}
openSUSE Build Service is sponsored by