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 <[email protected]>
---
 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 <[email protected]>
+ *
+ * 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

_______________________________________________
kernel mailing list
[email protected]
https://admin.fedoraproject.org/mailman/listinfo/kernel

Reply via email to