vici: Add command to load a private key from a token
authorTobias Brunner <tobias@strongswan.org>
Fri, 18 Nov 2016 14:01:18 +0000 (15:01 +0100)
committerTobias Brunner <tobias@strongswan.org>
Thu, 16 Feb 2017 18:24:07 +0000 (19:24 +0100)
PINs are stored in a "hidden" credential set, so that its shared
secrets are not exposed via VICI.  Since they are not explicitly loaded as
shared secrets via VICI a client might consider them as removed secrets and
remove them.

src/libcharon/plugins/vici/README.md
src/libcharon/plugins/vici/vici_cred.c

index 5c29ed3..556d05e 100644 (file)
@@ -491,6 +491,24 @@ including keys found in other backends.
                ]
        }
 
+### load-token() ###
+
+Load a private key located on a token into the daemon.  Such keys may be listed
+and unloaded using the _get-keys_ and _unload-key_ commands, respectively (based
+on the key identifier derived from the public key).
+
+       {
+               handle = <hex-encoded CKA_ID of the private key on token>
+               slot = <optional slot number>
+               module = <optional PKCS#11 module>
+               pin = <optional PIN to access the key, has to be provided via other
+                          means if not given>
+       } => {
+               success = <yes or no>
+               errmsg = <error string on failure>
+               id = <hex-encoded SHA-1 key identifier of the public key on success>
+       }
+
 ### load-shared() ###
 
 Load a shared IKE PSK, EAP or XAuth secret into the daemon.
index c8d7113..6c7c194 100644 (file)
@@ -57,6 +57,11 @@ struct private_vici_cred_t {
        mem_cred_t *creds;
 
        /**
+        * separate credential set for token PINs
+        */
+       mem_cred_t *pins;
+
+       /**
         * cache CRLs to disk?
         */
        bool cachecrl;
@@ -255,7 +260,7 @@ CALLBACK(unload_key, vici_message_t*,
        private_vici_cred_t *this, char *name, u_int id, vici_message_t *message)
 {
        chunk_t keyid;
-       char *hex, *msg = NULL;
+       char buf[BUF_LEN], *hex, *msg = NULL;
 
        hex = message->get_str(message, NULL, "id");
        if (!hex)
@@ -263,8 +268,13 @@ CALLBACK(unload_key, vici_message_t*,
                return create_reply("key id missing");
        }
        keyid = chunk_from_hex(chunk_from_str(hex), NULL);
-       DBG1(DBG_CFG, "unloaded private key with id %+B", &keyid);
-       if (!this->creds->remove_key(this->creds, keyid))
+       snprintf(buf, sizeof(buf), "%+B", &keyid);
+       DBG1(DBG_CFG, "unloaded private key with id %s", buf);
+       if (this->creds->remove_key(this->creds, keyid))
+       {       /* also remove any potential PIN associated with this id */
+               this->pins->remove_shared_unique(this->pins, buf);
+       }
+       else
        {
                msg = "key not found";
        }
@@ -298,6 +308,87 @@ CALLBACK(get_keys, vici_message_t*,
        return builder->finalize(builder);
 }
 
+CALLBACK(load_token, vici_message_t*,
+       private_vici_cred_t *this, char *name, u_int id, vici_message_t *message)
+{
+       vici_builder_t *builder;
+       private_key_t *key;
+       shared_key_t *shared = NULL;
+       identification_t *owner;
+       mem_cred_t *set = NULL;
+       chunk_t handle, fp;
+       char buf[BUF_LEN], *hex, *module, *pin, *unique = NULL;
+       int slot;
+
+       hex = message->get_str(message, NULL, "handle");
+       if (!hex)
+       {
+               return create_reply("keyid missing");
+       }
+       handle = chunk_from_hex(chunk_from_str(hex), NULL);
+       slot = message->get_int(message, -1, "slot");
+       module = message->get_str(message, NULL, "module");
+       pin = message->get_str(message, NULL, "pin");
+
+       if (pin)
+       {       /* provide the pin in a temporary credential set to access the key */
+               shared = shared_key_create(SHARED_PIN, chunk_clone(chunk_from_str(pin)));
+               owner = identification_create_from_encoding(ID_KEY_ID, handle);
+               set = mem_cred_create();
+               set->add_shared(set, shared->get_ref(shared), owner, NULL);
+               lib->credmgr->add_local_set(lib->credmgr, &set->set, FALSE);
+       }
+       if (slot >= 0)
+       {
+               key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
+                                               BUILD_PKCS11_KEYID, handle,
+                                               BUILD_PKCS11_SLOT, slot,
+                                               module ? BUILD_PKCS11_MODULE : BUILD_END, module,
+                                               BUILD_END);
+       }
+       else
+       {
+               key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
+                                               BUILD_PKCS11_KEYID, handle,
+                                               module ? BUILD_PKCS11_MODULE : BUILD_END, module,
+                                               BUILD_END);
+       }
+       if (set)
+       {
+               lib->credmgr->remove_local_set(lib->credmgr, &set->set);
+               set->destroy(set);
+       }
+       if (!key)
+       {
+               chunk_free(&handle);
+               DESTROY_IF(shared);
+               return create_reply("loading private key from token failed");
+       }
+       builder = vici_builder_create();
+       builder->add_kv(builder, "success", "yes");
+       if (key->get_fingerprint(key, KEYID_PUBKEY_SHA1, &fp))
+       {
+               snprintf(buf, sizeof(buf), "%+B", &fp);
+               builder->add_kv(builder, "id", "%s", buf);
+               unique = buf;
+       }
+       if (shared && unique)
+       {       /* use the handle as owner, but the key identifier as unique ID */
+               owner = identification_create_from_encoding(ID_KEY_ID, handle);
+               this->pins->add_shared_unique(this->pins, unique, shared,
+                                                                       linked_list_create_with_items(owner, NULL));
+       }
+       else
+       {
+               DESTROY_IF(shared);
+       }
+       DBG1(DBG_CFG, "loaded %N private key from token", key_type_names,
+                key->get_type(key));
+       this->creds->add_key(this->creds, key);
+       chunk_free(&handle);
+       return builder->finalize(builder);
+}
+
 CALLBACK(shared_owners, bool,
        linked_list_t *owners, vici_message_t *message, char *name, chunk_t value)
 {
@@ -474,6 +565,7 @@ static void manage_commands(private_vici_cred_t *this, bool reg)
        manage_command(this, "load-key", load_key, reg);
        manage_command(this, "unload-key", unload_key, reg);
        manage_command(this, "get-keys", get_keys, reg);
+       manage_command(this, "load-token", load_token, reg);
        manage_command(this, "load-shared", load_shared, reg);
        manage_command(this, "unload-shared", unload_shared, reg);
        manage_command(this, "get-shared", get_shared, reg);
@@ -492,6 +584,8 @@ METHOD(vici_cred_t, destroy, void,
 
        lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
        this->creds->destroy(this->creds);
+       lib->credmgr->remove_set(lib->credmgr, &this->pins->set);
+       this->pins->destroy(this->pins);
        free(this);
 }
 
@@ -516,6 +610,7 @@ vici_cred_t *vici_cred_create(vici_dispatcher_t *dispatcher)
                },
                .dispatcher = dispatcher,
                .creds = mem_cred_create(),
+               .pins = mem_cred_create(),
        );
 
        if (lib->settings->get_bool(lib->settings, "%s.cache_crls", FALSE, lib->ns))
@@ -524,6 +619,7 @@ vici_cred_t *vici_cred_create(vici_dispatcher_t *dispatcher)
                DBG1(DBG_CFG, "crl caching to %s enabled", CRL_DIR);
        }
        lib->credmgr->add_set(lib->credmgr, &this->creds->set);
+       lib->credmgr->add_set(lib->credmgr, &this->pins->set);
 
        manage_commands(this, TRUE);