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

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

Reply via email to