From f05f7d656b13c3904f0c55e86ebe9e9b19fcd222 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Thu, 28 Jun 2018 14:41:55 +0200 Subject: [PATCH] zcryptctl: new tool zcryptctl for multiple zcrypt node management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Jan Höppner --- zconf/zcrypt/Makefile | 7 +- zconf/zcrypt/zcryptctl.8 | 147 ++++++ zconf/zcrypt/zcryptctl.c | 1030 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 1182 insertions(+), 2 deletions(-) create mode 100644 zconf/zcrypt/zcryptctl.8 create mode 100644 zconf/zcrypt/zcryptctl.c diff --git a/zconf/zcrypt/Makefile b/zconf/zcrypt/Makefile index 698e1485..d075f349 100644 --- 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 diff --git a/zconf/zcrypt/zcryptctl.8 b/zconf/zcrypt/zcryptctl.8 new file mode 100644 index 00000000..5d098da3 --- /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=" +.IP "aps=" +.IP "doms=" +.IP "ioctls=" +.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) diff --git a/zconf/zcrypt/zcryptctl.c b/zconf/zcrypt/zcryptctl.c new file mode 100644 index 00000000..8326a087 --- /dev/null +++ b/zconf/zcrypt/zcryptctl.c @@ -0,0 +1,1030 @@ +/* + * zcryptctl - Maintain zcrypt multi device nodes. + * + * by Harald Freudenberger + * 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 +#include +#include + +#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_\n" + "with 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 ", + .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 ", + .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 ", + .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 ", + .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 ", + .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 ", + .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 ", + .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 ", + .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=\n" + " aps=\n" + " doms=\n" + " ioctls=\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; +}