[ima-evm-utils 3/5] evmctl: Allow signing using external crypto engine

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


evmctl uses openssl API for signing a file. One might want to use a external
crypto engine for signing. For example, I am trying to use a smartcard for
signing. Openssl provides engine API to deal with external crypto.

This patch adds support where one can specify external engine to load and
then sign a file using that.

For example, I am doing this on my machine.

#Sign a file
evmctl ima_sign -e pkcs11 -x -v --key slot_1-id_bb82253c8ab1337a74b95a6c68da4a658859c71a /tmp/data.txt -p <enter-pin> --engine_so /usr/lib64/openssl/engines/engine_pkcs11.so --engine_module opensc-pkcs11.so

Or

evmctl ima_sign -v -x --key id_bb82253c8ab1337a74b95a6c68da4a658859c71a  /tmp/data.txt -p <enter-pin> -e pkcs11 --engine_so /usr/lib64/openssl/engines/engine_pkcs11.so --engine_module opensc-pkcs11.so -t "OpenSC Card (Fedora Signing CA)" --pkcs11_module opensc-pkcs11.so

# Verify signature
evmctl ima_verify /tmp/data.txt -k signer-x509-cert.der

Signed-off-by: Vivek Goyal <vgoyal at redhat.com>
---
 configure.ac    |   1 +
 src/Makefile.am |   2 +-
 src/evmctl.c    | 283 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 280 insertions(+), 6 deletions(-)

diff --git a/configure.ac b/configure.ac
index 5decc4f..137c88a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -27,6 +27,7 @@ AC_HEADER_STDC
 PKG_CHECK_MODULES(OPENSSL, [ openssl >= 0.9.8 ])
 AC_SUBST(OPENSSL_CFLAGS)
 AC_SUBST(OPENSSL_LIBS)
+PKG_CHECK_MODULES(OPENSC,libp11)
 AC_CHECK_HEADER(unistd.h)
 AC_CHECK_HEADERS(openssl/conf.h)
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 6779baf..472479f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,7 +4,7 @@ bin_PROGRAMS = evmctl
 evmctl_SOURCES = evmctl.c
 evmctl_CPPFLAGS = $(OPENSSL_CFLAGS)
 evmctl_LDFLAGS = $(LDFLAGS_READLINE)
-evmctl_LDADD =  $(OPENSSL_LIBS) -lkeyutils
+evmctl_LDADD =  $(OPENSSL_LIBS) -lkeyutils -lp11
 
 INCLUDES = -I$(top_srcdir) -include config.h
 
diff --git a/src/evmctl.c b/src/evmctl.c
index 3679e68..2205d1e 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -46,6 +46,7 @@
 #include <dirent.h>
 #include <ctype.h>
 #include <stdbool.h>
+#include <libp11.h>
 
 #include <openssl/sha.h>
 #include <openssl/rsa.h>
@@ -260,7 +261,7 @@ static int digest;
 static int digsig;
 static const char *hash_algo = "sha1";
 static int user_hash_algo;
-static char *keypass;
+static char *keypass = NULL;
 static int sigfile;
 static int modsig;
 static char *uuid_str;
@@ -268,6 +269,12 @@ static int x509;
 static int user_sig_type;
 static char *keyfile;
 static bool memlock = false;
+static char *engine_id = NULL;
+static char *engine_so = NULL;
+static char *engine_module = NULL;
+static ENGINE *engine = NULL;
+static  char *token_label = NULL;
+static  char *pkcs11_module = NULL;
 
 typedef int (*sign_hash_fn_t)(const char *algo, const unsigned char *hash, int size, const char *keyfile, unsigned char *sig);
 
@@ -475,11 +482,34 @@ static void calc_keyid_v2(uint32_t *keyid, char *str, RSA *key)
 	free(pkey);
 }
 
+static RSA *read_priv_key_from_engine(ENGINE *e, const char *keyfile)
+{
+	RSA *key;
+	EVP_PKEY *evp_key = NULL;
+
+	evp_key = ENGINE_load_private_key(e, keyfile, NULL, NULL);
+	if (!evp_key) {
+		log_err("ENGINE_load_private_key: key=%s failed\n", keyfile);
+		return NULL;
+	}
+
+	key = EVP_PKEY_get1_RSA(evp_key);
+	if (!key) {
+		log_err("Getting RSA key from EVP_PKEY failed\n");
+		return NULL;
+	}
+
+	return key;
+}
+
 static RSA *read_priv_key(const char *keyfile)
 {
 	FILE *fp;
 	RSA *key;
 
+	if (engine_id != NULL)
+		return read_priv_key_from_engine(engine, keyfile);
+
 	fp = fopen(keyfile, "r");
 	if (!fp) {
 		log_err("Unable to open keyfile %s\n", keyfile);
@@ -992,6 +1022,205 @@ static int calc_hash(const char *file, uint8_t *hash)
 	return mdlen;
 }
 
+static int token_label_to_slot_id(char *tk_label)
+{
+	int rc = -1;
+	PKCS11_CTX *ctx;
+	PKCS11_SLOT *slots, *slot, *temp_slots;
+	unsigned int nslots, temp_nslots;
+
+	if (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, 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",
+					token_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(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(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 void unload_engine(ENGINE *e)
+{
+	/* Free up functional reference */
+	if (e != NULL)
+		ENGINE_finish(e);
+
+	ENGINE_cleanup();
+}
+
+static ENGINE *load_engine(void)
+{
+	ENGINE *e;
+
+	if (engine_id == NULL) {
+		log_err("Provide an engine_id\n");
+		return NULL;
+	}
+
+	if (engine_so == NULL) {
+		log_err("Provide engine shared library (engine_so)\n");
+		return NULL;
+	}
+
+	if (engine_module == NULL) {
+		log_err("Provide engine module (engine_module)\n");
+		return NULL;
+	}
+
+	ENGINE_load_dynamic();
+
+	/* TODO: Allow using built-in engines */
+	e = ENGINE_by_id("dynamic");
+	if (e == NULL) {
+		log_err("ENGINE_by_id(dynamic) failed\n");
+		return NULL;
+	}
+
+	if (!ENGINE_ctrl_cmd_string(e, "SO_PATH", engine_so, 0)) {
+		log_err("ENGINE_ctrl_cmd_string(SO_PATH,%s) failed\n",
+					engine_so);
+		goto out;
+	}
+
+	if (!ENGINE_ctrl_cmd_string(e, "ID", engine_id, 0)) {
+		log_err("ENGINE_ctrl_cmd_string(ID,%s) failed\n",
+					engine_id);
+		goto out;
+	}
+
+	if (!ENGINE_ctrl_cmd_string(e, "LIST_ADD", "1", 0)) {
+		log_err("ENGINE_ctrl_cmd_string(LIST_ADD,1) failed\n");
+		goto out;
+	}
+
+	if (!ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0)) {
+		log_err("ENGINE_ctrl_cmd_string(LOAD) failed\n");
+		goto out;
+	}
+
+	if (!ENGINE_ctrl_cmd_string(e, "MODULE_PATH", engine_module, 0)) {
+		log_err("ENGINE_ctrl_cmd_string(MODULE_PATH,%s) failed\n",
+					engine_module);
+		goto out;
+	}
+
+	log_info("Using engine with engine_id: %s\n", ENGINE_get_id(e));
+
+	ENGINE_set_default(e, ENGINE_METHOD_ALL);
+
+	if (!ENGINE_init(e)) {
+		log_err("ENGINE_init(e) failed\n");
+		goto out;
+	}
+
+	/* Engine initialized. Free up structural reference */
+	ENGINE_free(e);
+
+	/* Use keypass as PIN to login into the card */
+	if (keypass != NULL)
+		ENGINE_ctrl_cmd_string(e, "PIN", keypass, 0);
+
+
+	ENGINE_set_default(e, ENGINE_METHOD_RSA);
+	return e;
+
+out:
+	if (e)
+		ENGINE_free(e);
+	return NULL;
+}
+
 static int hash_ima(const char *file)
 {
 	unsigned char hash[65] = "\x01"; /* MAX hash size + 1 */
@@ -1761,6 +1990,11 @@ static void usage(void)
 		"  -u, --uuid         use file system UUID in HMAC calculation (EVM v2)\n"
 		"  -n                 print result to stdout instead of setting xattr\n"
 		"  -l, --memlock      run executable file locked in memory.\n"
+		"  -e, --engine_id    Specify engine id to use for signing (pkcs11)\n"
+		"  -g, --engine_so    Specify engine library/shared object file\n"
+		"  -b  --engine_module   Specify engine module file path\n"
+		"  -c, --pkcs11_module   Specify pkcs11 module file path\n"
+		"  -t, --token	      token label to use\n"
 		"  -v                 increase verbosity level\n"
 		"  -h, --help         display this help and exit\n"
 		"\n");
@@ -1793,6 +2027,11 @@ static struct option opts[] = {
 	{"x509", 0, 0, 'x'},
 	{"key", 1, 0, 'k'},
 	{"memlock", 0, 0, 'l'},
+	{"engine_id", 1, 0, 'e'},
+	{"engine_so", 1, 0, 'g'},
+	{"engine_module", 1, 0, 'b'},
+	{"pkcs11_module", 1, 0, 'c'},
+	{"token", 1, 0, 't'},
 	{}
 
 };
@@ -1808,7 +2047,8 @@ int main(int argc, char *argv[])
 	verify_hash = verify_hash_v1;
 
 	while (1) {
-		c = getopt_long(argc, argv, "hvnsda:p:fu::xk:l", opts, &lind);
+		c = getopt_long(argc, argv, "hvnsda:p:fu::xk:le:t:c:g:b:",
+						opts, &lind);
 		if (c == -1)
 			break;
 
@@ -1861,6 +2101,21 @@ int main(int argc, char *argv[])
 		case 'l':
 			memlock = true;
 			break;
+		case 'e':
+			engine_id = optarg;
+			break;
+		case 'g':
+			engine_so = optarg;
+			break;
+		case 'b':
+			engine_module = optarg;
+			break;
+		case 't':
+			token_label = optarg;
+			break;
+		case 'c':
+			pkcs11_module = optarg;
+			break;
 		case '?':
 			exit(1);
 			break;
@@ -1872,10 +2127,26 @@ int main(int argc, char *argv[])
 	OpenSSL_add_all_algorithms();
 	ERR_load_crypto_strings();
 
-	if (argv[optind] == NULL)
+	if (argv[optind] == NULL) {
 		usage();
-	else
-		err = call_command(cmds, argv[optind++]);
+		goto out;
+	}
+
+	if (token_label) {
+		char *temp_key = strdup(keyfile);
+		err = parse_token_label(token_label, temp_key, &keyfile);
+		free(temp_key);
+		if (err)
+			goto out;
+	}
+
+	if (engine_id) {
+		engine = load_engine();
+		if (engine == NULL)
+			goto out;
+	}
+
+	err = call_command(cmds, argv[optind++]);
 
 	if (err) {
 		unsigned long error;
@@ -1889,8 +2160,10 @@ int main(int argc, char *argv[])
 		}
 	}
 
+out:
 	ERR_free_strings();
 	EVP_cleanup();
+	unload_engine(engine);
 
 	return err;
 }
-- 
1.8.3.1



More information about the kernel mailing list