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@xxxxxxxxxx> --- 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 kernel@xxxxxxxxxxxxxxxxxxxxxxx https://admin.fedoraproject.org/mailman/listinfo/kernel