[ima-evm-utils 5/5] evmctl-client: A simple client to request signing from evmctl daemon

Vivek Goyal vgoyal at redhat.com
Fri Sep 6 19:38:24 UTC 2013


This is evmctl-client program which can connect to evmctl daemon and
request for file signing.

For example one could use it as follows.

# make sure /var/run/evmctl is present.
$ mkdir -p /var/run/evmctl

# start daemon. Use V2 signature with sha256 hash.
$ evmctl daemonize -x -a sha256

# Sign a file
$ evmctl-client ima_sign --key /etc/keys/privkey_evm.pem /tmp/data.txt

# verify signature of file
$ evmctl ima_verify /tmp/data.txt -k /etc/keys/x509_evm.der

Similar stuff could be done for signing using a key which is on crypto card.

- Make sure /var/run/evmctl exists
mkdir -p /var/run/evmctl

- Start daemon
evmctl daemonize -x -a sha256 --engine_id pkcs11 --engine_so /usr/lib64/openssl/engines/engine_pkcs11.so --engine_module opensc-pkcs11.so

- Set pin fort the smart card.
evmctl-client set_pin <card-pin>

- Sign file
evmctl-client ima_sign --key slot_1-id_bb82253c8ab1337a74b95a6c68da4a658859c71a /tmp/data.txt

Or

evmctl-client ima_sign --key id_bb82253c8ab74b95a6c68da4a658859c71a --token "OpenSC Card (Fedora Signing CA)" --pkcs11_module opensc-pkcs11.so /tmp/data.txt

- Verify file
evmctl ima_verify /tmp/data.txt --key /root/keys/fedora-signer-x509-cert.der

Signed-off-by: Vivek Goyal <vgoyal at redhat.com>
---
 src/Makefile.am |   7 +-
 src/client.c    | 697 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 703 insertions(+), 1 deletion(-)
 create mode 100644 src/client.c

diff --git a/src/Makefile.am b/src/Makefile.am
index 472479f..45d5ea8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,11 +1,16 @@
 
-bin_PROGRAMS = evmctl
+bin_PROGRAMS = evmctl evmctl-client
 
 evmctl_SOURCES = evmctl.c
 evmctl_CPPFLAGS = $(OPENSSL_CFLAGS)
 evmctl_LDFLAGS = $(LDFLAGS_READLINE)
 evmctl_LDADD =  $(OPENSSL_LIBS) -lkeyutils -lp11
 
+evmctl_client_SOURCES = client.c
+evmctl_client_CPPFLAGS = $(OPENSSL_CFLAGS)
+evmctl_client_LDFLAGS = $(LDFLAGS_READLINE)
+evmctl_client_LDADD =  $(OPENSSL_LIBS) -lkeyutils -lp11
+
 INCLUDES = -I$(top_srcdir) -include config.h
 
 DISTCLEANFILES = @DISTCLEANFILES@
diff --git a/src/client.c b/src/client.c
new file mode 100644
index 0000000..92bafa3
--- /dev/null
+++ b/src/client.c
@@ -0,0 +1,697 @@
+/*
+ * evm-utils - IMA/EVM support utilities
+ *
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * Authors:
+ * Vivek Goyal <vgoyal at redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ * File: client.c
+ *	 IMA/EVM client program
+ */
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <libp11.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/prctl.h>
+#include <poll.h>
+
+#include "daemon.h"
+
+#define USE_FPRINTF
+
+#ifdef DEBUG
+#define log_debug(fmt, args...)		do_log(LOG_DEBUG, "%s:%d " fmt, __func__ , __LINE__ , ##args)
+#else
+#define log_debug(fmt, args...)
+#define log_debug_dump(p, len)
+#endif
+
+#define log_info(fmt, args...)		do_log(LOG_INFO, fmt, ##args)
+#define log_warn(fmt, args...)		do_log(LOG_WARNING, fmt, ##args)
+#define log_notice(fmt, args...)	do_log(LOG_NOTICE, fmt, ##args)
+#define log_err(fmt, args...)		do_log(LOG_ERR, fmt, ##args)
+#define log_errno(fmt, args...)		do_log(LOG_ERR, fmt ": errno: %s (%d)\n", ##args, strerror(errno), errno)
+
+
+struct client_info {
+	char *key;
+	char *token_label;
+	bool memlock;
+	char *file;
+	bool detached;
+	char *pkcs11_module;
+};
+
+struct command {
+	char *name;
+	int (*func)(struct command *cmd, struct client_info *ci);
+	int cmd;
+	char *arg;
+	char *msg;		/* extra info message */
+};
+
+static int verbose = LOG_INFO - 1;
+static int g_argc;
+static char **g_argv;
+
+struct command cmds[];
+static void print_usage(struct command *cmd);
+
+static void do_log(int level, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+#ifdef USE_FPRINTF
+	vfprintf(stderr, fmt, ap);
+#else
+	vsyslog(level, fmt, ap);
+#endif
+	va_end(ap);
+}
+
+static int token_label_to_slot_id(struct client_info *ci, char *tk_label)
+{
+	int rc = -1;
+	PKCS11_CTX *ctx;
+	PKCS11_SLOT *slots, *slot, *temp_slots;
+	unsigned int nslots, temp_nslots;
+
+	if (ci->pkcs11_module == NULL) {
+		log_err("No pkcs11_module specified\n");
+		return -1;
+	}
+
+	ctx = PKCS11_CTX_new();
+
+	/* load pkcs #11 module */
+	rc = PKCS11_CTX_load(ctx, ci->pkcs11_module);
+        if (rc) {
+                log_err("Loading pkcs11 engine failed: %s\n",
+                        ERR_reason_error_string(ERR_get_error()));
+		rc = -1;
+		goto nolib;
+        }
+
+	/* get information on all slots */
+	rc = PKCS11_enumerate_slots(ctx, &slots, &nslots);
+	if (rc < 0) {
+		log_err("No slots available\n");
+                rc = -1;
+                goto noslots;
+        }
+
+	/* search for slot with given label */
+	temp_slots = slots;
+	temp_nslots = nslots;
+
+	while (1) {
+		/* get first slot with a token */
+		slot = PKCS11_find_token(ctx, temp_slots, temp_nslots);
+		if (!slot || !slot->token) {
+			log_err("No token available with given label:%s\n",
+					tk_label);
+			rc = -1;
+			goto notoken;
+		}
+
+		if (!strcmp(slot->token->label, tk_label)) {
+			rc = PKCS11_get_slotid_from_slot(slot);
+			break;
+		}
+
+		temp_slots = slot + 1;
+		temp_nslots -= temp_slots - slots;
+	}
+
+	log_info("token label=%s slot_id=%u\n", tk_label, rc);
+
+notoken:
+        PKCS11_release_all_slots(ctx, slots, nslots);
+noslots:
+	PKCS11_CTX_unload(ctx);
+nolib:
+	PKCS11_CTX_free(ctx);
+	return rc;
+}
+
+/*
+ * Parse a token determine slot and prefix slot info to supplied key in a
+ * format openssl likes. New key memmory is allocated and returned in
+ * newkey which should be freed by caller
+ */
+static int parse_token_label(struct client_info *ci,
+			char *tk_label, char *key, char **newkey)
+{
+	char *p;
+	char *modified_key;
+	int slot_id;
+	char slot_str[32];
+
+	/*
+	 * If user specified token label, convert that into slot and
+	 * prefix it to key in a format understood by openssl
+	 */
+	if (tk_label == NULL)
+		return 0;
+
+	slot_id = token_label_to_slot_id(ci, tk_label);
+	if (slot_id < 0)
+		return 1;
+
+	if (!strncmp(key, "id_", 3) || !strncmp(key, "label_", 6)) {
+		sprintf(slot_str, "slot_%d-", slot_id);
+	} else
+		sprintf(slot_str, "%d:", slot_id);
+
+	modified_key = malloc(strlen(slot_str) + strlen(key) + 1);
+	if (modified_key == NULL) {
+		log_err("Memory allocation failed\n");
+		return 1;
+	}
+
+	strcpy(modified_key, slot_str);
+	p = modified_key;
+	p += strlen(slot_str);
+	strcpy(p, key);
+	*newkey = modified_key;
+	log_info("Using key:%s\n", modified_key);
+	return 0;
+}
+
+static int check_response(int sd, char **srvmsg)
+{
+	ssize_t n;
+	struct msghdr msg;
+	struct iovec iov;
+	char buffer[1024];
+
+	evmctld_msghdr *em;
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+
+	memset(&msg, '\0', sizeof(msg));
+	memset(buffer, '\0', sizeof(buffer));
+
+	iov.iov_base = buffer;
+	iov.iov_len = 1023;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	n = recvmsg(sd, &msg, 0);
+	if (n < 0) {
+		fprintf(stderr, "evmctl-client: could not get response from "
+			"server: %m\n");
+		exit(1);
+	}
+
+	em = (evmctld_msghdr *)buffer;
+
+	if (em->version != EVMCTLD_VERSION) {
+		fprintf(stderr, "evmctl-client: got version %d, "
+			"expected version %d\n", em->version, EVMCTLD_VERSION);
+		exit(1);
+	}
+
+	if (em->command != CMD_RESPONSE) {
+		fprintf(stderr, "evmctl-client: got unexpected response: %d\n",
+			em->command);
+		exit(1);
+	}
+
+	evmctld_cmd_response *resp = (evmctld_cmd_response *)((uint8_t *)em +
+					offsetof(evmctld_msghdr, size) +
+					sizeof(em->size));
+
+	if (resp->rc == 0)
+		return 0;
+
+	*srvmsg = strdup((char *)resp->errmsg);
+	return resp->rc;
+}
+
+static void set_pin(int sd, char *pin)
+{
+	struct msghdr msg;
+	struct iovec iov[1];
+	evmctld_msghdr em;
+
+	uint32_t size0 = evmctld_string_size(pin);
+
+	em.version = EVMCTLD_VERSION;
+	em.command = CMD_SET_PIN;
+	em.size = size0;
+	iov[0].iov_base = &em;
+	iov[0].iov_len = sizeof (em);
+
+	memset(&msg, '\0', sizeof(msg));
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 1;
+
+	ssize_t n;
+	n = sendmsg(sd, &msg, 0);
+	if (n < 0) {
+		fprintf(stderr, "evmctl-client: set pin: sendmsg failed: "
+			"%m\n");
+		exit(1);
+	}
+
+	uint8_t *buffer = NULL;
+	buffer = calloc(1, size0);
+	if (!buffer) {
+		fprintf(stderr, "evmctl-client: could not allocate memory: "
+			"%m\n");
+		exit(1);
+	}
+
+	evmctld_string *ep = (evmctld_string *)buffer;
+	evmctld_string_set(ep, pin);
+	iov[0].iov_base = ep;
+	iov[0].iov_len = size0;
+
+	n = sendmsg(sd, &msg, 0);
+	if (n < 0) {
+		fprintf(stderr, "evmctl-client: set pin: sendmsg failed: "
+			"%m\n");
+		exit(1);
+	}
+
+	char *srvmsg = NULL;
+	int rc = check_response(sd, &srvmsg);
+	if (rc < 0) {
+		fprintf(stderr, "evmctl-client: %s\n",
+			srvmsg);
+		exit(1);
+	}
+
+	free(buffer);
+}
+
+static int connect_to_server(void)
+{
+	int rc = access(SOCKPATH, R_OK);
+	if (rc != 0) {
+		fprintf(stderr, "evmctl-client: could not connect to server: "
+			"%m\n");
+		exit(1);
+	}
+
+	struct sockaddr_un addr_un = {
+		.sun_family = AF_UNIX,
+		.sun_path = SOCKPATH,
+	};
+
+	int sd = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (sd < 0) {
+		fprintf(stderr, "pesign-client: could not open socket: %m\n");
+		exit(1);
+	}
+
+	socklen_t len = strlen(addr_un.sun_path) +
+			sizeof(addr_un.sun_family);
+
+	rc = connect(sd, (struct sockaddr *)&addr_un, len);
+	if (rc < 0) {
+		fprintf(stderr, "pesign-client: could not connect to daemon: "
+			"%m\n");
+		exit(1);
+	}
+
+	return sd;
+}
+
+/*
+ * Concanate two input strings to a destination. Also allocate memory for
+ * destination which needs to be freed by caller
+ */
+static char *strcat_alloc(char *src1, char *src2)
+{
+	int len;
+	char *dest;
+
+	len = strlen(src1) + strlen(src2) + 1;
+	dest = calloc(len, 1);
+	if (!dest) {
+		fprintf(stderr, "evmctl-client: failed to allocate memory.\n");
+		exit(1);
+	}
+
+	strcpy(dest, src1);
+	strcat(dest, src2);
+
+	return dest;
+}
+
+static int sign_ima(int sd, struct client_info *ci)
+{
+	struct msghdr msg;
+	int max_nr_strings = 4;
+	struct iovec iov[max_nr_strings]; /* Key, file, token, memlock str */
+	evmctld_msghdr em;
+	uint32_t total_size = 0;
+	int nr_strings = 0, len, idx;
+	char *strings[max_nr_strings], *temp;
+
+	if (!ci->key) {
+		fprintf(stderr, "evmctl-client: No key specified.\n");
+		exit(1);
+	}
+
+	memset(strings, 0, max_nr_strings * sizeof(char *));
+
+	/* key to use for signing */
+	nr_strings++;
+	idx = 0;
+	len = sizeof(evmctld_string) + strlen("--key ") + strlen(ci->key) + 1;
+	strings[idx] = calloc(len, 1);
+	if (!strings[idx]) {
+		fprintf(stderr, "evmctl-client: failed to allocate memory.\n");
+		exit(1);
+	}
+
+	temp = strcat_alloc("--key ", ci->key);
+	total_size += len;
+	evmctld_string_set((evmctld_string *)strings[idx], temp);
+	free(temp);
+
+	/* memlock info */
+	if (ci->memlock) {
+		nr_strings++;
+		idx++;
+		len = sizeof(evmctld_string) + strlen("--memlock") + 1;
+		strings[idx] = calloc(len, 1);
+		if (!strings[idx]) {
+			fprintf(stderr, "evmctl-client: failed to allocate"
+				" memory.\n");
+			exit(1);
+		}
+
+		total_size += len;
+		evmctld_string_set((evmctld_string *)strings[idx],
+						"--memlock");
+	}
+
+	/* File to be signed */
+	nr_strings++;
+	idx++;
+	len = sizeof(evmctld_string) + strlen(ci->file) + 1;
+	strings[idx] = calloc(len, 1);
+	if (!strings[idx]) {
+		fprintf(stderr, "evmctl-client: failed to allocate memory.\n");
+		exit(1);
+	}
+	evmctld_string_set((evmctld_string *)strings[idx], ci->file);
+	total_size += len;
+
+
+	em.version = EVMCTLD_VERSION;
+	if (ci->detached == true)
+		em.command = CMD_IMA_SIGN_DETACHED;
+	else
+		em.command = CMD_IMA_SIGN_ATTACHED;
+	em.size = total_size;
+	iov[0].iov_base = &em;
+	iov[0].iov_len = sizeof (em);
+
+	memset(&msg, '\0', sizeof(msg));
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 1;
+
+	ssize_t n;
+	n = sendmsg(sd, &msg, 0);
+	if (n < 0) {
+		fprintf(stderr, "evmctl-client: set pin: sendmsg failed: "
+			"%m\n");
+		exit(1);
+	}
+
+	for (idx = 0; idx < nr_strings; idx++) {
+		iov[idx].iov_base = strings[idx];
+		iov[idx].iov_len = ((evmctld_string *)strings[idx])->size +
+					sizeof(evmctld_string);
+	}
+
+	msg.msg_iov = iov;
+	msg.msg_iovlen = nr_strings;
+
+	n = sendmsg(sd, &msg, 0);
+	if (n < 0) {
+		fprintf(stderr, "evmctl-client: set pin: sendmsg failed: "
+			"%m\n");
+		exit(1);
+	}
+
+	char *srvmsg = NULL;
+	int rc = check_response(sd, &srvmsg);
+	if (rc < 0) {
+		fprintf(stderr, "evmctl-client: %s\n",
+			srvmsg);
+		exit(1);
+	}
+
+	/* Free strings array */
+	for (idx = 0; idx < max_nr_strings; idx++)
+		free(strings[idx]);
+
+	return 0;
+}
+
+static int cmd_ima_sign(struct command *cmd, struct client_info *ci)
+{
+	char *file = g_argv[optind++];
+	int sd = -1, rc = 0;
+
+	if (!file) {
+		log_err("Parameters missing\n");
+		print_usage(cmd);
+		return -1;
+	}
+
+	ci->file = file;
+
+	if (!ci->key) {
+		log_err("Key missing\n");
+		print_usage(cmd);
+		return -1;
+	}
+
+	/* If a token is specified, convert it to slot id */
+	if (ci->token_label) {
+		char *temp = strdup(ci->key);
+		if (!temp) {
+			log_err("Memory allocation failed\n");
+			return -ENOMEM;
+		}
+		rc = parse_token_label(ci, ci->token_label, temp, &ci->key);
+		free(temp);
+		if (rc) {
+			log_err("Parsing token label failed\n");
+			return rc;
+		}
+	}
+	sd = connect_to_server();
+
+	return sign_ima(sd, ci);
+
+}
+
+static int cmd_set_pin(struct command *cmd, struct client_info *ci)
+{
+	char *pin = g_argv[optind++];
+	int sd = -1;
+
+	if (!pin) {
+		log_err("Parameters missing\n");
+		print_usage(cmd);
+		return -1;
+	}
+
+	sd = connect_to_server();
+	set_pin(sd, pin);
+	return 0;
+}
+
+static void print_usage(struct command *cmd)
+{
+	printf("usage: %s %s\n", cmd->name, cmd->arg ? cmd->arg : "");
+}
+
+static void print_full_usage(struct command *cmd)
+{
+	if (cmd->name)
+		printf("usage: %s %s\n", cmd->name, cmd->arg ? cmd->arg : "");
+	if (cmd->msg)
+		printf("%s", cmd->msg);
+}
+
+static int print_command_usage(struct command *cmds, char *command)
+{
+	struct command *cmd;
+
+	for (cmd = cmds; cmd->name; cmd++) {
+		if (strcmp(cmd->name, command) == 0) {
+			print_full_usage(cmd);
+			return 0;
+		}
+	}
+	printf("invalid command: %s\n", command);
+	return -1;
+}
+
+static void print_all_usage(struct command *cmds)
+{
+	struct command *cmd;
+
+	printf("commands:\n");
+
+	for (cmd = cmds; cmd->name; cmd++) {
+		if (cmd->arg)
+			printf(" %s %s\n", cmd->name, cmd->arg);
+		else if (cmd->msg)
+			printf(" %s", cmd->msg);
+	}
+}
+
+static int
+call_command(struct command *cmds, char *command, struct client_info *ci)
+{
+	struct command *cmd;
+
+	for (cmd = cmds; cmd->name; cmd++) {
+		if (strcasecmp(cmd->name, command) == 0)
+			return cmd->func(cmd, ci);
+	}
+	printf("Invalid command: %s\n", command);
+	return -1;
+}
+
+static int cmd_help(struct command *cmd, struct client_info *ci)
+{
+	if (!g_argv[optind]) {
+		print_usage(cmd);
+		return 0;
+	} else
+		return print_command_usage(cmds, g_argv[optind]);
+}
+
+static void usage(void)
+{
+	printf("Usage: evmctl [-v] <command> [OPTIONS]\n");
+
+	print_all_usage(cmds);
+
+	printf(
+		"\n"
+		"  -k, --key          path to signing key (default keys are /etc/keys/{privkey,pubkey}_evm.pem)\n"
+		"  -l, --memlock      run executable file locked in memory.\n"
+		"  -t, --token	      token label to use\n"
+		"  -f, --sigfile      Generate detached signature.\n"
+		"  -c, --pkcs11_module Specify pkcs11 module to use. Needed if token label is used.\n"
+		"  -v                 increase verbosity level\n"
+		"  -h, --help         display this help and exit\n"
+		"\n");
+}
+
+struct command cmds[] = {
+	{"help", cmd_help, 0, "<command>"},
+	{"set_pin", cmd_set_pin, 0, "pin", "Set evmctld engine pin.\n"},
+	{"ima_sign", cmd_ima_sign, 0, "--key key [--token token_label] [--sigfile] [--memlock] file", "Make file content signature.\n"},
+	{0, 0, 0, NULL}
+};
+
+static struct option opts[] = {
+	{"help", 0, 0, 'h'},
+	{"sigfile", 0, 0, 'f'},
+	{"key", 1, 0, 'k'},
+	{"memlock", 0, 0, 'l'},
+	{"token", 1, 0, 't'},
+	{"pkcs11_module", 1, 0, 'c'},
+	{}
+
+};
+
+int main(int argc, char *argv[])
+{
+	int err = 0, c, lind;
+	struct client_info client_info, *ci;
+
+
+	g_argv = argv;
+	g_argc = argc;
+
+	ci = &client_info;
+	memset(ci, 0, sizeof(struct client_info));
+
+	while (1) {
+		c = getopt_long(argc, argv, "hfk:lt:", opts, &lind);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			usage();
+			exit(0);
+			break;
+		case 'v':
+			verbose++;
+			break;
+		case 'f':
+			ci->detached = true;
+			break;
+		case 'k':
+			ci->key = optarg;
+			break;
+		case 'l':
+			ci->memlock = true;
+			break;
+		case 't':
+			ci->token_label = optarg;
+			break;
+		case 'c':
+			ci->pkcs11_module = optarg;
+			break;
+		case '?':
+			exit(1);
+			break;
+		default:
+			log_err("getopt() returned: %d (%c)\n", c, c);
+		}
+	}
+
+	if (argv[optind] == NULL)
+		usage();
+	else
+		err = call_command(cmds, argv[optind++], ci);
+
+	if (err)
+		log_err("evmctl-client exited with error %d\n, err");
+
+	return err;
+}
-- 
1.8.3.1



More information about the kernel mailing list