stroke: Allow specifying the ipsec.secrets location in strongswan.conf
[strongswan.git] / src / libcharon / plugins / stroke / stroke_cred.c
index 91e71f1..83431d1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Tobias Brunner
+ * Copyright (C) 2008-2013 Tobias Brunner
  * Copyright (C) 2008 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <limits.h>
-#include <glob.h>
-#include <libgen.h>
-#include <sys/mman.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <unistd.h>
 
+#ifdef HAVE_GLOB_H
+#include <glob.h>
+#endif
+
 #include "stroke_cred.h"
-#include "stroke_shared_key.h"
 
 #include <credentials/certificates/x509.h>
 #include <credentials/certificates/crl.h>
 #include <credentials/certificates/ac.h>
+#include <credentials/containers/pkcs12.h>
 #include <credentials/sets/mem_cred.h>
 #include <credentials/sets/callback_cred.h>
-#include <utils/linked_list.h>
+#include <collections/linked_list.h>
 #include <utils/lexparser.h>
 #include <threading/rwlock.h>
 #include <daemon.h>
@@ -64,24 +65,20 @@ struct private_stroke_cred_t {
        stroke_cred_t public;
 
        /**
-        * list of trusted peer/signer/CA certificates (certificate_t)
-        */
-       linked_list_t *certs;
-
-       /**
-        * list of shared secrets (private_shared_key_t)
+        * secrets file with credential information
         */
-       linked_list_t *shared;
+       char *secrets_file;
 
        /**
-        * list of private keys (private_key_t)
+        * credentials
         */
-       linked_list_t *private;
+       mem_cred_t *creds;
 
        /**
-        * read-write lock to lists
+        * ignore missing CA basic constraint (i.e. treat all certificates in
+        * ipsec.conf ca sections and ipsec.d/cacerts as CA certificates)
         */
-       rwlock_t *lock;
+       bool force_ca_cert;
 
        /**
         * cache CRLs to disk?
@@ -89,258 +86,138 @@ struct private_stroke_cred_t {
        bool cachecrl;
 };
 
-/**
- * data to pass to various filters
- */
-typedef struct {
-       private_stroke_cred_t *this;
-       identification_t *id;
-       certificate_type_t cert;
-       key_type_t key;
-} id_data_t;
+/** Length of smartcard specifier parts (module, keyid) */
+#define SC_PART_LEN 128
 
 /**
- * destroy id enumerator data and unlock list
+ * Kind of smartcard specifier token
  */
-static void id_data_destroy(id_data_t *data)
-{
-       data->this->lock->unlock(data->this->lock);
-       free(data);
-}
+typedef enum {
+       SC_FORMAT_SLOT_MODULE_KEYID,
+       SC_FORMAT_SLOT_KEYID,
+       SC_FORMAT_KEYID,
+       SC_FORMAT_INVALID,
+} smartcard_format_t;
 
 /**
- * filter function for private key enumerator
+ * Parse a smartcard specifier token
  */
-static bool private_filter(id_data_t *data,
-                                                  private_key_t **in, private_key_t **out)
+static smartcard_format_t parse_smartcard(char *smartcard, u_int *slot,
+                                                                                 char *module, char *keyid)
 {
-       private_key_t *key;
+       /* The token has one of the following three formats:
+        * - %smartcard<slot>@<module>:<keyid>
+        * - %smartcard<slot>:<keyid>
+        * - %smartcard:<keyid>
+        */
+       char buf[2 * SC_PART_LEN], *pos;
 
-       key = *in;
-       if (data->key == KEY_ANY || data->key == key->get_type(key))
+       if (sscanf(smartcard, "%%smartcard%u@%255s", slot, buf) == 2)
        {
-               if (data->id == NULL)
-               {
-                       *out = key;
-                       return TRUE;
-               }
-               if (key->has_fingerprint(key, data->id->get_encoding(data->id)))
+               pos = strchr(buf, ':');
+               if (!pos)
                {
-                       *out = key;
-                       return TRUE;
+                       return SC_FORMAT_INVALID;
                }
+               *pos++ = '\0';
+               snprintf(module, SC_PART_LEN, "%s", buf);
+               snprintf(keyid, SC_PART_LEN, "%s", pos);
+               return SC_FORMAT_SLOT_MODULE_KEYID;
        }
-       return FALSE;
-}
-
-/**
- * Implements credential_set_t.create_private_enumerator
- */
-static enumerator_t* create_private_enumerator(private_stroke_cred_t *this,
-                                                       key_type_t type, identification_t *id)
-{
-       id_data_t *data;
-
-       data = malloc_thing(id_data_t);
-       data->this = this;
-       data->id = id;
-       data->key = type;
-
-       this->lock->read_lock(this->lock);
-       return enumerator_create_filter(this->private->create_enumerator(this->private),
-                                                                       (void*)private_filter, data,
-                                                                       (void*)id_data_destroy);
-}
-
-/**
- * filter function for certs enumerator
- */
-static bool certs_filter(id_data_t *data, certificate_t **in, certificate_t **out)
-{
-       public_key_t *public;
-       certificate_t *cert = *in;
-
-       if (data->cert != CERT_ANY && data->cert != cert->get_type(cert))
-       {
-               return FALSE;
-       }
-       if (data->id == NULL || cert->has_subject(cert, data->id))
+       if (sscanf(smartcard, "%%smartcard%u:%127s", slot, keyid) == 2)
        {
-               *out = *in;
-               return TRUE;
+               return SC_FORMAT_SLOT_KEYID;
        }
-
-       public = cert->get_public_key(cert);
-       if (public)
+       if (sscanf(smartcard, "%%smartcard:%127s", keyid) == 1)
        {
-               if (data->key == KEY_ANY || data->key != public->get_type(public))
-               {
-                       if (public->has_fingerprint(public, data->id->get_encoding(data->id)))
-                       {
-                               public->destroy(public);
-                               *out = *in;
-                               return TRUE;
-                       }
-               }
-               public->destroy(public);
+               return SC_FORMAT_KEYID;
        }
-       return FALSE;
+       return SC_FORMAT_INVALID;
 }
 
 /**
- * Implements credential_set_t.create_cert_enumerator
+ * Load a credential from a smartcard
  */
-static enumerator_t* create_cert_enumerator(private_stroke_cred_t *this,
-                                                       certificate_type_t cert, key_type_t key,
-                                                       identification_t *id, bool trusted)
+static certificate_t *load_from_smartcard(smartcard_format_t format,
+                                                                                 u_int slot, char *module, char *keyid,
+                                                                                 credential_type_t type, int subtype)
 {
-       id_data_t *data;
+       chunk_t chunk;
+       void *cred;
 
-       if (trusted && (cert == CERT_X509_CRL || cert == CERT_X509_AC))
+       chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
+       switch (format)
        {
-               return NULL;
+               case SC_FORMAT_SLOT_MODULE_KEYID:
+                       cred = lib->creds->create(lib->creds, type, subtype,
+                                                       BUILD_PKCS11_SLOT, slot,
+                                                       BUILD_PKCS11_MODULE, module,
+                                                       BUILD_PKCS11_KEYID, chunk, BUILD_END);
+                       break;
+               case SC_FORMAT_SLOT_KEYID:
+                       cred = lib->creds->create(lib->creds, type, subtype,
+                                                       BUILD_PKCS11_SLOT, slot,
+                                                       BUILD_PKCS11_KEYID, chunk, BUILD_END);
+                       break;
+               case SC_FORMAT_KEYID:
+                       cred = lib->creds->create(lib->creds, type, subtype,
+                                                       BUILD_PKCS11_KEYID, chunk, BUILD_END);
+                       break;
+               default:
+                       cred = NULL;
+                       break;
        }
-       data = malloc_thing(id_data_t);
-       data->this = this;
-       data->id = id;
-       data->cert = cert;
-       data->key = key;
-
-       this->lock->read_lock(this->lock);
-       return enumerator_create_filter(this->certs->create_enumerator(this->certs),
-                                                                       (void*)certs_filter, data,
-                                                                       (void*)id_data_destroy);
-}
-
-typedef struct {
-       private_stroke_cred_t *this;
-       identification_t *me;
-       identification_t *other;
-       shared_key_type_t type;
-} shared_data_t;
+       free(chunk.ptr);
 
-/**
- * free shared key enumerator data and unlock list
- */
-static void shared_data_destroy(shared_data_t *data)
-{
-       data->this->lock->unlock(data->this->lock);
-       free(data);
+       return cred;
 }
 
-/**
- * filter function for certs enumerator
- */
-static bool shared_filter(shared_data_t *data,
-                                                 stroke_shared_key_t **in, shared_key_t **out,
-                                                 void **unused1, id_match_t *me,
-                                                 void **unused2, id_match_t *other)
+METHOD(stroke_cred_t, load_ca, certificate_t*,
+       private_stroke_cred_t *this, char *filename)
 {
-       id_match_t my_match = ID_MATCH_NONE, other_match = ID_MATCH_NONE;
-       stroke_shared_key_t *stroke = *in;
-       shared_key_t *shared = &stroke->shared;
+       certificate_t *cert = NULL;
+       char path[PATH_MAX];
 
-       if (data->type != SHARED_ANY && shared->get_type(shared) != data->type)
+       if (strpfx(filename, "%smartcard"))
        {
-               return FALSE;
-       }
+               smartcard_format_t format;
+               char module[SC_PART_LEN], keyid[SC_PART_LEN];
+               u_int slot;
 
-       if (data->me)
-       {
-               my_match = stroke->has_owner(stroke, data->me);
-       }
-       if (data->other)
-       {
-               other_match = stroke->has_owner(stroke, data->other);
-       }
-       if ((data->me || data->other) && (!my_match && !other_match))
-       {
-               return FALSE;
-       }
-       *out = shared;
-       if (me)
-       {
-               *me = my_match;
-       }
-       if (other)
-       {
-               *other = other_match;
-       }
-       return TRUE;
-}
-
-/**
- * Implements credential_set_t.create_shared_enumerator
- */
-static enumerator_t* create_shared_enumerator(private_stroke_cred_t *this,
-                                                       shared_key_type_t type, identification_t *me,
-                                                       identification_t *other)
-{
-       shared_data_t *data = malloc_thing(shared_data_t);
-
-       data->this = this;
-       data->me = me;
-       data->other = other;
-       data->type = type;
-       this->lock->read_lock(this->lock);
-       return enumerator_create_filter(this->shared->create_enumerator(this->shared),
-                                                                       (void*)shared_filter, data,
-                                                                       (void*)shared_data_destroy);
-}
-
-/**
- * Add a certificate to chain
- */
-static certificate_t* add_cert(private_stroke_cred_t *this, certificate_t *cert)
-{
-       certificate_t *current;
-       enumerator_t *enumerator;
-       bool new = TRUE;
-
-       this->lock->read_lock(this->lock);
-       enumerator = this->certs->create_enumerator(this->certs);
-       while (enumerator->enumerate(enumerator, (void**)&current))
-       {
-               if (current->equals(current, cert))
+               format = parse_smartcard(filename, &slot, module, keyid);
+               if (format != SC_FORMAT_INVALID)
                {
-                       /* cert already in queue */
-                       cert->destroy(cert);
-                       cert = current;
-                       new = FALSE;
-                       break;
+                       cert = (certificate_t*)load_from_smartcard(format,
+                                                       slot, module, keyid, CRED_CERTIFICATE, CERT_X509);
                }
        }
-       enumerator->destroy(enumerator);
-
-       if (new)
-       {
-               this->certs->insert_last(this->certs, cert);
-       }
-       this->lock->unlock(this->lock);
-       return cert;
-}
-
-/**
- * Implementation of stroke_cred_t.load_ca.
- */
-static certificate_t* load_ca(private_stroke_cred_t *this, char *filename)
-{
-       certificate_t *cert;
-       char path[PATH_MAX];
-
-       if (*filename == '/')
-       {
-               snprintf(path, sizeof(path), "%s", filename);
-       }
        else
        {
-               snprintf(path, sizeof(path), "%s/%s", CA_CERTIFICATE_DIR, filename);
-       }
+               if (*filename == '/')
+               {
+                       snprintf(path, sizeof(path), "%s", filename);
+               }
+               else
+               {
+                       snprintf(path, sizeof(path), "%s/%s", CA_CERTIFICATE_DIR, filename);
+               }
 
-       cert = lib->creds->create(lib->creds,
-                                                         CRED_CERTIFICATE, CERT_X509,
-                                                         BUILD_FROM_FILE, path,
-                                                         BUILD_END);
+               if (this->force_ca_cert)
+               {       /* we treat this certificate as a CA certificate even if it has no
+                        * CA basic constraint */
+                       cert = lib->creds->create(lib->creds,
+                                                                 CRED_CERTIFICATE, CERT_X509,
+                                                                 BUILD_FROM_FILE, path, BUILD_X509_FLAG, X509_CA,
+                                                                 BUILD_END);
+               }
+               else
+               {
+                       cert = lib->creds->create(lib->creds,
+                                                                 CRED_CERTIFICATE, CERT_X509,
+                                                                 BUILD_FROM_FILE, path,
+                                                                 BUILD_END);
+               }
+       }
        if (cert)
        {
                x509_t *x509 = (x509_t*)cert;
@@ -352,113 +229,147 @@ static certificate_t* load_ca(private_stroke_cred_t *this, char *filename)
                        cert->destroy(cert);
                        return NULL;
                }
-               return (certificate_t*)add_cert(this, cert);
+               DBG1(DBG_CFG, "  loaded ca certificate \"%Y\" from '%s'",
+                        cert->get_subject(cert), filename);
+               return this->creds->add_cert_ref(this->creds, TRUE, cert);
        }
        return NULL;
 }
 
-/**
- * Add X.509 CRL to chain
- */
-static bool add_crl(private_stroke_cred_t *this, crl_t* crl)
+METHOD(stroke_cred_t, load_peer, certificate_t*,
+       private_stroke_cred_t *this, char *filename)
 {
-       certificate_t *current, *cert = &crl->certificate;
-       enumerator_t *enumerator;
-       bool new = TRUE, found = FALSE;
+       certificate_t *cert = NULL;
+       char path[PATH_MAX];
 
-       this->lock->write_lock(this->lock);
-       enumerator = this->certs->create_enumerator(this->certs);
-       while (enumerator->enumerate(enumerator, (void**)&current))
+       if (strpfx(filename, "%smartcard"))
        {
-               if (current->get_type(current) == CERT_X509_CRL)
-               {
-                       crl_t *crl_c = (crl_t*)current;
-                       chunk_t authkey = crl->get_authKeyIdentifier(crl);
-                       chunk_t authkey_c = crl_c->get_authKeyIdentifier(crl_c);
+               smartcard_format_t format;
+               char module[SC_PART_LEN], keyid[SC_PART_LEN];
+               u_int slot;
 
-                       /* if compare authorityKeyIdentifiers if available */
-                       if (authkey.ptr && authkey_c.ptr && chunk_equals(authkey, authkey_c))
-                       {
-                               found = TRUE;
-                       }
-                       else
-                       {
-                               identification_t *issuer = cert->get_issuer(cert);
-                               identification_t *issuer_c = current->get_issuer(current);
-
-                               /* otherwise compare issuer distinguished names */
-                               if (issuer->equals(issuer, issuer_c))
-                               {
-                                       found = TRUE;
-                               }
-                       }
-                       if (found)
-                       {
-                               new = crl_is_newer(crl, crl_c);
-                               if (new)
-                               {
-                                       this->certs->remove_at(this->certs, enumerator);
-                               }
-                               else
-                               {
-                                       cert->destroy(cert);
-                               }
-                               break;
-                       }
+               format = parse_smartcard(filename, &slot, module, keyid);
+               if (format != SC_FORMAT_INVALID)
+               {
+                       cert = (certificate_t*)load_from_smartcard(format,
+                                                       slot, module, keyid, CRED_CERTIFICATE, CERT_X509);
                }
        }
-       enumerator->destroy(enumerator);
+       else
+       {
+               if (*filename == '/')
+               {
+                       snprintf(path, sizeof(path), "%s", filename);
+               }
+               else
+               {
+                       snprintf(path, sizeof(path), "%s/%s", CERTIFICATE_DIR, filename);
+               }
 
-       if (new)
+               cert = lib->creds->create(lib->creds,
+                                                                 CRED_CERTIFICATE, CERT_ANY,
+                                                                 BUILD_FROM_FILE, path,
+                                                                 BUILD_END);
+       }
+       if (cert)
        {
-               this->certs->insert_last(this->certs, cert);
+               cert = this->creds->add_cert_ref(this->creds, TRUE, cert);
+               DBG1(DBG_CFG, "  loaded certificate \"%Y\" from '%s'",
+                                         cert->get_subject(cert), filename);
+               return cert;
        }
-       this->lock->unlock(this->lock);
-       return new;
-}
-
-/**
- * Add X.509 attribute certificate to chain
- */
-static bool add_ac(private_stroke_cred_t *this, ac_t* ac)
-{
-       certificate_t *cert = &ac->certificate;
-
-       this->lock->write_lock(this->lock);
-       this->certs->insert_last(this->certs, cert);
-       this->lock->unlock(this->lock);
-       return TRUE;
+       DBG1(DBG_CFG, "  loading certificate from '%s' failed", filename);
+       return NULL;
 }
 
-/**
- * Implementation of stroke_cred_t.load_peer.
- */
-static certificate_t* load_peer(private_stroke_cred_t *this, char *filename)
+METHOD(stroke_cred_t, load_pubkey, certificate_t*,
+       private_stroke_cred_t *this, char *filename, identification_t *identity)
 {
        certificate_t *cert;
+       public_key_t *key;
        char path[PATH_MAX];
+       builder_part_t build_part;
+       key_type_t type = KEY_ANY;
 
-       if (*filename == '/')
+       if (streq(filename, "%dns"))
        {
-               snprintf(path, sizeof(path), "%s", filename);
+               return NULL;
+       }
+       if (strncaseeq(filename, "dns:", 4))
+       {       /* RFC 3110 format */
+               build_part = BUILD_BLOB_DNSKEY;
+               /* not a complete RR, only RSA supported */
+               type = KEY_RSA;
+               filename += 4;
+       }
+       else if (strncaseeq(filename, "ssh:", 4))
+       {       /* SSH key */
+               build_part = BUILD_BLOB_SSHKEY;
+               filename += 4;
        }
        else
-       {
-               snprintf(path, sizeof(path), "%s/%s", CERTIFICATE_DIR, filename);
+       {       /* try PKCS#1 by default */
+               build_part = BUILD_BLOB_ASN1_DER;
        }
+       if (strncaseeq(filename, "0x", 2) || strncaseeq(filename, "0s", 2))
+       {
+               chunk_t printable_key, raw_key;
 
-       cert = lib->creds->create(lib->creds,
-                                                         CRED_CERTIFICATE, CERT_ANY,
-                                                         BUILD_FROM_FILE, path,
-                                                         BUILD_END);
-       if (cert)
+               printable_key = chunk_create(filename + 2, strlen(filename) - 2);
+               raw_key = strncaseeq(filename, "0x", 2) ?
+                                                                chunk_from_hex(printable_key, NULL) :
+                                                                chunk_from_base64(printable_key, NULL);
+               key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, type,
+                                                                build_part, raw_key, BUILD_END);
+               chunk_free(&raw_key);
+               if (key)
+               {
+                       cert = lib->creds->create(lib->creds, CRED_CERTIFICATE,
+                                                                         CERT_TRUSTED_PUBKEY,
+                                                                         BUILD_PUBLIC_KEY, key,
+                                                                         BUILD_SUBJECT, identity,
+                                                                         BUILD_END);
+                       type = key->get_type(key);
+                       key->destroy(key);
+                       if (cert)
+                       {
+                               cert = this->creds->add_cert_ref(this->creds, TRUE, cert);
+                               DBG1(DBG_CFG, "  loaded %N public key for \"%Y\"",
+                                        key_type_names, type, identity);
+                               return cert;
+                       }
+               }
+               DBG1(DBG_CFG, "  loading public key for \"%Y\" failed", identity);
+       }
+       else
        {
-               cert = add_cert(this, cert);
-               DBG1(DBG_CFG, "  loaded certificate \"%Y\" from '%s'",
-                                         cert->get_subject(cert), filename);
-               return cert->get_ref(cert);
+               if (*filename == '/')
+               {
+                       snprintf(path, sizeof(path), "%s", filename);
+               }
+               else
+               {
+                       snprintf(path, sizeof(path), "%s/%s", CERTIFICATE_DIR, filename);
+               }
+
+               cert = lib->creds->create(lib->creds,
+                                                                 CRED_CERTIFICATE, CERT_TRUSTED_PUBKEY,
+                                                                 BUILD_FROM_FILE, path,
+                                                                 BUILD_SUBJECT, identity,
+                                                                 BUILD_END);
+               if (cert)
+               {
+                       cert = this->creds->add_cert_ref(this->creds, TRUE, cert);
+                       key = cert->get_public_key(cert);
+                       type = key->get_type(key);
+                       key->destroy(key);
+                       DBG1(DBG_CFG, "  loaded %N public key for \"%Y\" from '%s'",
+                                key_type_names, type, identity, filename);
+                       return cert;
+               }
+               DBG1(DBG_CFG, "  loading public key for \"%Y\" from '%s' failed",
+                        identity, filename);
        }
-       DBG1(DBG_CFG, "  loading certificate from '%s' failed", filename);
        return NULL;
 }
 
@@ -492,11 +403,21 @@ static void load_certdir(private_stroke_cred_t *this, char *path,
                {
                        case CERT_X509:
                                if (flag & X509_CA)
-                               {       /* for CA certificates, we strictly require
-                                        * the CA basic constraint to be set */
-                                       cert = lib->creds->create(lib->creds,
+                               {
+                                       if (this->force_ca_cert)
+                                       {       /* treat this certificate as CA cert even it has no
+                                                * CA basic constraint */
+                                               cert = lib->creds->create(lib->creds,
+                                                                               CRED_CERTIFICATE, CERT_X509,
+                                                                               BUILD_FROM_FILE, file, BUILD_X509_FLAG,
+                                                                               X509_CA, BUILD_END);
+                                       }
+                                       else
+                                       {
+                                               cert = lib->creds->create(lib->creds,
                                                                                CRED_CERTIFICATE, CERT_X509,
                                                                                BUILD_FROM_FILE, file, BUILD_END);
+                                       }
                                        if (cert)
                                        {
                                                x509_t *x509 = (x509_t*)cert;
@@ -511,8 +432,8 @@ static void load_certdir(private_stroke_cred_t *this, char *path,
                                                }
                                                else
                                                {
-                                                       DBG1(DBG_CFG, "  loaded ca certificate \"%Y\" from '%s'",
-                                                                                 cert->get_subject(cert), file);
+                                                       DBG1(DBG_CFG, "  loaded ca certificate \"%Y\" "
+                                                                "from '%s'", cert->get_subject(cert), file);
                                                }
                                        }
                                        else
@@ -540,7 +461,7 @@ static void load_certdir(private_stroke_cred_t *this, char *path,
                                }
                                if (cert)
                                {
-                                       add_cert(this, cert);
+                                       this->creds->add_cert(this->creds, TRUE, cert);
                                }
                                break;
                        case CERT_X509_CRL:
@@ -550,7 +471,7 @@ static void load_certdir(private_stroke_cred_t *this, char *path,
                                                                                  BUILD_END);
                                if (cert)
                                {
-                                       add_crl(this, (crl_t*)cert);
+                                       this->creds->add_crl(this->creds, (crl_t*)cert);
                                        DBG1(DBG_CFG, "  loaded crl from '%s'",  file);
                                }
                                else
@@ -565,7 +486,7 @@ static void load_certdir(private_stroke_cred_t *this, char *path,
                                                                                  BUILD_END);
                                if (cert)
                                {
-                                       add_ac(this, (ac_t*)cert);
+                                       this->creds->add_cert(this->creds, FALSE, cert);
                                        DBG1(DBG_CFG, "  loaded attribute certificate from '%s'",
                                                                  file);
                                }
@@ -582,10 +503,8 @@ static void load_certdir(private_stroke_cred_t *this, char *path,
        enumerator->destroy(enumerator);
 }
 
-/**
- * Implementation of credential_set_t.cache_cert.
- */
-static void cache_cert(private_stroke_cred_t *this, certificate_t *cert)
+METHOD(stroke_cred_t, cache_cert, void,
+       private_stroke_cred_t *this, certificate_t *cert)
 {
        if (cert->get_type(cert) == CERT_X509_CRL && this->cachecrl)
        {
@@ -593,29 +512,36 @@ static void cache_cert(private_stroke_cred_t *this, certificate_t *cert)
                crl_t *crl = (crl_t*)cert;
 
                cert->get_ref(cert);
-               if (add_crl(this, crl))
+               if (this->creds->add_crl(this->creds, crl))
                {
                        char buf[BUF_LEN];
                        chunk_t chunk, hex;
 
                        chunk = crl->get_authKeyIdentifier(crl);
                        hex = chunk_to_hex(chunk, NULL, FALSE);
-                       snprintf(buf, sizeof(buf), "%s/%s.crl", CRL_DIR, hex);
+                       snprintf(buf, sizeof(buf), "%s/%s.crl", CRL_DIR, hex.ptr);
                        free(hex.ptr);
 
                        if (cert->get_encoding(cert, CERT_ASN1_DER, &chunk))
                        {
-                               chunk_write(chunk, buf, "crl", 022, TRUE);
+                               if (chunk_write(chunk, buf, 022, TRUE))
+                               {
+                                       DBG1(DBG_CFG, "  written crl file '%s' (%d bytes)",
+                                                buf, chunk.len);
+                               }
+                               else
+                               {
+                                       DBG1(DBG_CFG, "  writing crl file '%s' failed: %s",
+                                                buf, strerror(errno));
+                               }
                                free(chunk.ptr);
                        }
                }
        }
 }
 
-/**
- * Implementation of stroke_cred_t.cachecrl.
- */
-static void cachecrl(private_stroke_cred_t *this, bool enabled)
+METHOD(stroke_cred_t, cachecrl, void,
+       private_stroke_cred_t *this, bool enabled)
 {
        DBG1(DBG_CFG, "crl caching to %s %s",
                 CRL_DIR, enabled ? "enabled" : "disabled");
@@ -686,8 +612,12 @@ static err_t extract_secret(chunk_t *secret, chunk_t *line)
  * Data for passphrase callback
  */
 typedef struct {
+       /** cached passphrases */
+       mem_cred_t *cache;
        /** socket we use for prompting */
        FILE *prompt;
+       /** type of secret to unlock */
+       int type;
        /** private key file */
        char *path;
        /** number of tries */
@@ -695,13 +625,15 @@ typedef struct {
 } passphrase_cb_data_t;
 
 /**
- * Callback function to receive Passphrases
+ * Callback function to receive passphrases
  */
 static shared_key_t* passphrase_cb(passphrase_cb_data_t *data,
-                                                               shared_key_type_t type,
-                                                               identification_t *me, identification_t *other,
-                                                               id_match_t *match_me, id_match_t *match_other)
+                                                                  shared_key_type_t type, identification_t *me,
+                                                                  identification_t *other, id_match_t *match_me,
+                                                                  id_match_t *match_other)
 {
+       static const int max_tries = 3;
+       shared_key_t *shared;
        chunk_t secret;
        char buf[256];
 
@@ -710,17 +642,23 @@ static shared_key_t* passphrase_cb(passphrase_cb_data_t *data,
                return NULL;
        }
 
+       data->try++;
+       if (data->try > max_tries + 1)
+       {       /* another builder might call this after we gave up, fail silently */
+               return NULL;
+       }
+       if (data->try > max_tries)
+       {
+               fprintf(data->prompt, "Passphrase invalid, giving up.\n");
+               return NULL;
+       }
        if (data->try > 1)
        {
-               if (data->try > 5)
-               {
-                       fprintf(data->prompt, "PIN invalid, giving up.\n");
-                       return NULL;
-               }
-               fprintf(data->prompt, "PIN invalid!\n");
+               fprintf(data->prompt, "Passphrase invalid!\n");
        }
-       data->try++;
-       fprintf(data->prompt, "Private key '%s' is encrypted.\n", data->path);
+       fprintf(data->prompt, "%s '%s' is encrypted.\n",
+                       data->type == CRED_PRIVATE_KEY ? "Private key" : "PKCS#12 file",
+                       data->path);
        fprintf(data->prompt, "Passphrase:\n");
        if (fgets(buf, sizeof(buf), data->prompt))
        {
@@ -736,7 +674,10 @@ static shared_key_t* passphrase_cb(passphrase_cb_data_t *data,
                        {
                                *match_other = ID_MATCH_NONE;
                        }
-                       return shared_key_create(SHARED_PRIVATE_KEY_PASS, chunk_clone(secret));
+                       shared = shared_key_create(SHARED_PRIVATE_KEY_PASS,
+                                                                          chunk_clone(secret));
+                       data->cache->add_shared(data->cache, shared->get_ref(shared), NULL);
+                       return shared;
                }
        }
        return NULL;
@@ -776,12 +717,12 @@ static shared_key_t* pin_cb(pin_cb_data_t *data, shared_key_type_t type,
                return NULL;
        }
 
+       data->try++;
        if (data->try > 1)
        {
                fprintf(data->prompt, "PIN invalid, aborting.\n");
                return NULL;
        }
-       data->try++;
        fprintf(data->prompt, "Login to '%s' required\n", data->card);
        fprintf(data->prompt, "PIN:\n");
        if (fgets(buf, sizeof(buf), data->prompt))
@@ -807,11 +748,11 @@ static shared_key_t* pin_cb(pin_cb_data_t *data, shared_key_type_t type,
 /**
  * Load a smartcard with a PIN
  */
-static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
+static bool load_pin(mem_cred_t *secrets, chunk_t line, int line_nr,
                                         FILE *prompt)
 {
        chunk_t sc = chunk_empty, secret = chunk_empty;
-       char smartcard[64], keyid[64], module[64], *pos;
+       char smartcard[BUF_LEN], keyid[SC_PART_LEN], module[SC_PART_LEN];
        private_key_t *key = NULL;
        u_int slot;
        chunk_t chunk;
@@ -820,11 +761,7 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
        mem_cred_t *mem = NULL;
        callback_cred_t *cb = NULL;
        pin_cb_data_t pin_data;
-       enum {
-               SC_FORMAT_SLOT_MODULE_KEYID,
-               SC_FORMAT_SLOT_KEYID,
-               SC_FORMAT_KEYID,
-       } format;
+       smartcard_format_t format;
 
        err_t ugh = extract_value(&sc, &line);
 
@@ -838,36 +775,11 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
                DBG1(DBG_CFG, "line %d: expected %%smartcard specifier", line_nr);
                return FALSE;
        }
-       snprintf(smartcard, sizeof(smartcard), "%.*s", sc.len, sc.ptr);
+       snprintf(smartcard, sizeof(smartcard), "%.*s", (int)sc.len, sc.ptr);
        smartcard[sizeof(smartcard) - 1] = '\0';
 
-       /* parse slot and key id. Three formats are supported:
-        * - %smartcard<slot>@<module>:<keyid>
-        * - %smartcard<slot>:<keyid>
-        * - %smartcard:<keyid>
-        */
-       if (sscanf(smartcard, "%%smartcard%u@%s", &slot, module) == 2)
-       {
-               pos = strchr(module, ':');
-               if (!pos)
-               {
-                       DBG1(DBG_CFG, "line %d: the given %%smartcard specifier is "
-                                "invalid", line_nr);
-                       return FALSE;
-               }
-               *pos = '\0';
-               strcpy(keyid, pos + 1);
-               format = SC_FORMAT_SLOT_MODULE_KEYID;
-       }
-       else if (sscanf(smartcard, "%%smartcard%u:%s", &slot, keyid) == 2)
-       {
-               format = SC_FORMAT_SLOT_KEYID;
-       }
-       else if (sscanf(smartcard, "%%smartcard:%s", keyid) == 1)
-       {
-               format = SC_FORMAT_KEYID;
-       }
-       else
+       format = parse_smartcard(smartcard, &slot, module, keyid);
+       if (format == SC_FORMAT_INVALID)
        {
                DBG1(DBG_CFG, "line %d: the given %%smartcard specifier is not"
                                " supported or invalid", line_nr);
@@ -887,21 +799,21 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
        }
 
        chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
-       if (secret.len == 7 && strneq(secret.ptr, "%prompt", 7))
+       if (secret.len == 7 && strpfx(secret.ptr, "%prompt"))
        {
                free(secret.ptr);
                if (!prompt)
                {       /* no IO channel to prompt, skip */
-                       free(chunk.ptr);
+                       chunk_clear(&chunk);
                        return TRUE;
                }
                /* use callback credential set to prompt for the pin */
                pin_data.prompt = prompt;
                pin_data.card = smartcard;
                pin_data.keyid = chunk;
-               pin_data.try = 1;
+               pin_data.try = 0;
                cb = callback_cred_create_shared((void*)pin_cb, &pin_data);
-               lib->credmgr->add_local_set(lib->credmgr, &cb->set);
+               lib->credmgr->add_local_set(lib->credmgr, &cb->set, FALSE);
        }
        else
        {
@@ -910,33 +822,12 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
                id = identification_create_from_encoding(ID_KEY_ID, chunk);
                mem = mem_cred_create();
                mem->add_shared(mem, shared, id, NULL);
-               lib->credmgr->add_local_set(lib->credmgr, &mem->set);
+               lib->credmgr->add_local_set(lib->credmgr, &mem->set, FALSE);
        }
 
        /* unlock: smartcard needs the pin and potentially calls public set */
-       this->lock->unlock(this->lock);
-       switch (format)
-       {
-               case SC_FORMAT_SLOT_MODULE_KEYID:
-                       key = lib->creds->create(lib->creds,
-                                                       CRED_PRIVATE_KEY, KEY_ANY,
-                                                       BUILD_PKCS11_SLOT, slot,
-                                                       BUILD_PKCS11_MODULE, module,
-                                                       BUILD_PKCS11_KEYID, chunk, BUILD_END);
-                       break;
-               case SC_FORMAT_SLOT_KEYID:
-                       key = lib->creds->create(lib->creds,
-                                                       CRED_PRIVATE_KEY, KEY_ANY,
-                                                       BUILD_PKCS11_SLOT, slot,
-                                                       BUILD_PKCS11_KEYID, chunk, BUILD_END);
-                       break;
-               case SC_FORMAT_KEYID:
-                       key = lib->creds->create(lib->creds,
-                                                       CRED_PRIVATE_KEY, KEY_ANY,
-                                                       BUILD_PKCS11_KEYID, chunk, BUILD_END);
-                       break;
-       }
-       this->lock->write_lock(this->lock);
+       key = (private_key_t*)load_from_smartcard(format, slot, module, keyid,
+                                                                                         CRED_PRIVATE_KEY, KEY_ANY);
        if (mem)
        {
                lib->credmgr->remove_local_set(lib->credmgr, &mem->set);
@@ -947,25 +838,25 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
                lib->credmgr->remove_local_set(lib->credmgr, &cb->set);
                cb->destroy(cb);
        }
+       chunk_clear(&chunk);
 
        if (key)
        {
-               DBG1(DBG_CFG, "  loaded private key from %.*s", sc.len, sc.ptr);
-               this->private->insert_last(this->private, key);
+               DBG1(DBG_CFG, "  loaded private key from %.*s", (int)sc.len, sc.ptr);
+               secrets->add_key(secrets, key);
        }
        return TRUE;
 }
 
 /**
- * Load a private key
+ * Load a private key or PKCS#12 container from a file
  */
-static bool load_private(private_stroke_cred_t *this, chunk_t line, int line_nr,
-                                                FILE *prompt, key_type_t key_type)
+static bool load_from_file(chunk_t line, int line_nr, FILE *prompt,
+                                                  char *path, int type, int subtype,
+                                                  void **result)
 {
-       char path[PATH_MAX];
        chunk_t filename;
        chunk_t secret = chunk_empty;
-       private_key_t *key;
 
        err_t ugh = extract_value(&filename, &line);
 
@@ -982,13 +873,13 @@ static bool load_private(private_stroke_cred_t *this, chunk_t line, int line_nr,
        if (*filename.ptr == '/')
        {
                /* absolute path name */
-               snprintf(path, sizeof(path), "%.*s", filename.len, filename.ptr);
+               snprintf(path, PATH_MAX, "%.*s", (int)filename.len, filename.ptr);
        }
        else
        {
                /* relative path name */
-               snprintf(path, sizeof(path), "%s/%.*s", PRIVATE_KEY_DIR,
-                                filename.len, filename.ptr);
+               snprintf(path, PATH_MAX, "%s/%.*s", PRIVATE_KEY_DIR,
+                                (int)filename.len, filename.ptr);
        }
 
        /* check for optional passphrase */
@@ -1001,35 +892,37 @@ static bool load_private(private_stroke_cred_t *this, chunk_t line, int line_nr,
                        return FALSE;
                }
        }
-       if (secret.len == 7 && strneq(secret.ptr, "%prompt", 7))
+       if (secret.len == 7 && strpfx(secret.ptr, "%prompt"))
        {
-               callback_cred_t *cb = NULL;
+               callback_cred_t *cb;
                passphrase_cb_data_t pp_data = {
                        .prompt = prompt,
+                       .type = type,
                        .path = path,
-                       .try = 1,
+                       .try = 0,
                };
 
                free(secret.ptr);
                if (!prompt)
                {
+                       *result = NULL;
                        return TRUE;
                }
+               /* add cache first so if valid passphrases are needed multiple times
+                * the callback is not called anymore */
+               pp_data.cache = mem_cred_create();
+               lib->credmgr->add_local_set(lib->credmgr, &pp_data.cache->set, FALSE);
                /* use callback credential set to prompt for the passphrase */
-               pp_data.prompt = prompt;
-               pp_data.path = path;
-               pp_data.try = 1;
                cb = callback_cred_create_shared((void*)passphrase_cb, &pp_data);
-               lib->credmgr->add_local_set(lib->credmgr, &cb->set);
+               lib->credmgr->add_local_set(lib->credmgr, &cb->set, FALSE);
 
-               /* unlock, as the builder might ask for a secret */
-               this->lock->unlock(this->lock);
-               key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, key_type,
-                                                                BUILD_FROM_FILE, path, BUILD_END);
-               this->lock->write_lock(this->lock);
+               *result = lib->creds->create(lib->creds, type, subtype,
+                                                                        BUILD_FROM_FILE, path, BUILD_END);
 
                lib->credmgr->remove_local_set(lib->credmgr, &cb->set);
                cb->destroy(cb);
+               lib->credmgr->remove_local_set(lib->credmgr, &pp_data.cache->set);
+               pp_data.cache->destroy(pp_data.cache);
        }
        else
        {
@@ -1040,22 +933,49 @@ static bool load_private(private_stroke_cred_t *this, chunk_t line, int line_nr,
                shared = shared_key_create(SHARED_PRIVATE_KEY_PASS, secret);
                mem = mem_cred_create();
                mem->add_shared(mem, shared, NULL);
-               lib->credmgr->add_local_set(lib->credmgr, &mem->set);
+               if (eat_whitespace(&line))
+               {       /* if there is a second passphrase add that too, could be needed for
+                        * PKCS#12 files using different passwords for MAC and encryption */
+                       ugh = extract_secret(&secret, &line);
+                       if (ugh != NULL)
+                       {
+                               DBG1(DBG_CFG, "line %d: malformed passphrase: %s", line_nr, ugh);
+                               mem->destroy(mem);
+                               return FALSE;
+                       }
+                       shared = shared_key_create(SHARED_PRIVATE_KEY_PASS, secret);
+                       mem->add_shared(mem, shared, NULL);
+               }
+               lib->credmgr->add_local_set(lib->credmgr, &mem->set, FALSE);
 
-               /* unlock, as the builder might ask for a secret */
-               this->lock->unlock(this->lock);
-               key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, key_type,
-                                                                BUILD_FROM_FILE, path, BUILD_END);
-               this->lock->write_lock(this->lock);
+               *result = lib->creds->create(lib->creds, type, subtype,
+                                                                        BUILD_FROM_FILE, path, BUILD_END);
 
                lib->credmgr->remove_local_set(lib->credmgr, &mem->set);
                mem->destroy(mem);
        }
+       return TRUE;
+}
+
+/**
+ * Load a private key
+ */
+static bool load_private(mem_cred_t *secrets, chunk_t line, int line_nr,
+                                                FILE *prompt, key_type_t key_type)
+{
+       char path[PATH_MAX];
+       private_key_t *key;
+
+       if (!load_from_file(line, line_nr, prompt, path, CRED_PRIVATE_KEY,
+                                               key_type, (void**)&key))
+       {
+               return FALSE;
+       }
        if (key)
        {
                DBG1(DBG_CFG, "  loaded %N private key from '%s'",
                         key_type_names, key->get_type(key), path);
-               this->private->insert_last(this->private, key);
+               secrets->add_key(secrets, key);
        }
        else
        {
@@ -1065,12 +985,65 @@ static bool load_private(private_stroke_cred_t *this, chunk_t line, int line_nr,
 }
 
 /**
+ * Load a PKCS#12 container
+ */
+static bool load_pkcs12(private_stroke_cred_t *this, mem_cred_t *secrets,
+                                               chunk_t line, int line_nr, FILE *prompt)
+{
+       enumerator_t *enumerator;
+       char path[PATH_MAX];
+       certificate_t *cert;
+       private_key_t *key;
+       pkcs12_t *pkcs12;
+
+       if (!load_from_file(line, line_nr, prompt, path, CRED_CONTAINER,
+                                               CONTAINER_PKCS12, (void**)&pkcs12))
+       {
+               return FALSE;
+       }
+       if (!pkcs12)
+       {
+               DBG1(DBG_CFG, "  loading credentials from '%s' failed", path);
+               return TRUE;
+       }
+       enumerator = pkcs12->create_cert_enumerator(pkcs12);
+       while (enumerator->enumerate(enumerator, &cert))
+       {
+               x509_t *x509 = (x509_t*)cert;
+
+               if (x509->get_flags(x509) & X509_CA)
+               {
+                       DBG1(DBG_CFG, "  loaded ca certificate \"%Y\" from '%s'",
+                                cert->get_subject(cert), path);
+               }
+               else
+               {
+                       DBG1(DBG_CFG, "  loaded certificate \"%Y\" from '%s'",
+                                cert->get_subject(cert), path);
+               }
+               this->creds->add_cert(this->creds, TRUE, cert->get_ref(cert));
+       }
+       enumerator->destroy(enumerator);
+       enumerator = pkcs12->create_key_enumerator(pkcs12);
+       while (enumerator->enumerate(enumerator, &key))
+       {
+               DBG1(DBG_CFG, "  loaded %N private key from '%s'",
+                        key_type_names, key->get_type(key), path);
+               secrets->add_key(secrets, key->get_ref(key));
+       }
+       enumerator->destroy(enumerator);
+       pkcs12->container.destroy(&pkcs12->container);
+       return TRUE;
+}
+
+/**
  * Load a shared key
  */
-static bool load_shared(private_stroke_cred_t *this, chunk_t line, int line_nr,
+static bool load_shared(mem_cred_t *secrets, chunk_t line, int line_nr,
                                                shared_key_type_t type, chunk_t ids)
 {
-       stroke_shared_key_t *shared_key;
+       shared_key_t *shared_key;
+       linked_list_t *owners;
        chunk_t secret = chunk_empty;
        bool any = TRUE;
 
@@ -1080,12 +1053,12 @@ static bool load_shared(private_stroke_cred_t *this, chunk_t line, int line_nr,
                DBG1(DBG_CFG, "line %d: malformed secret: %s", line_nr, ugh);
                return FALSE;
        }
-       shared_key = stroke_shared_key_create(type, secret);
+       shared_key = shared_key_create(type, secret);
        DBG1(DBG_CFG, "  loaded %N secret for %s", shared_key_type_names, type,
                 ids.len > 0 ? (char*)ids.ptr : "%any");
        DBG4(DBG_CFG, "  secret: %#B", &secret);
 
-       this->shared->insert_last(this->shared, shared_key);
+       owners = linked_list_create();
        while (ids.len > 0)
        {
                chunk_t id;
@@ -1095,6 +1068,8 @@ static bool load_shared(private_stroke_cred_t *this, chunk_t line, int line_nr,
                if (ugh != NULL)
                {
                        DBG1(DBG_CFG, "line %d: %s", line_nr, ugh);
+                       shared_key->destroy(shared_key);
+                       owners->destroy_offset(owners, offsetof(identification_t, destroy));
                        return FALSE;
                }
                if (id.len == 0)
@@ -1111,72 +1086,42 @@ static bool load_shared(private_stroke_cred_t *this, chunk_t line, int line_nr,
                        continue;
                }
 
-               shared_key->add_owner(shared_key, peer_id);
+               owners->insert_last(owners, peer_id);
                any = FALSE;
        }
        if (any)
        {
-               shared_key->add_owner(shared_key,
+               owners->insert_last(owners,
                                        identification_create_from_encoding(ID_ANY, chunk_empty));
        }
+       secrets->add_shared_list(secrets, shared_key, owners);
        return TRUE;
 }
 
 /**
  * reload ipsec.secrets
  */
-static void load_secrets(private_stroke_cred_t *this, char *file, int level,
-                                                FILE *prompt)
+static void load_secrets(private_stroke_cred_t *this, mem_cred_t *secrets,
+                                                char *file, int level, FILE *prompt)
 {
-       int line_nr = 0, fd;
-       chunk_t src, line;
-       private_key_t *private;
-       shared_key_t *shared;
-       struct stat sb;
-       void *addr;
+       int line_nr = 0;
+       chunk_t *src, line;
 
        DBG1(DBG_CFG, "loading secrets from '%s'", file);
-       fd = open(file, O_RDONLY);
-       if (fd == -1)
+       src = chunk_map(file, FALSE);
+       if (!src)
        {
                DBG1(DBG_CFG, "opening secrets file '%s' failed: %s", file,
                         strerror(errno));
                return;
        }
-       if (fstat(fd, &sb) == -1)
-       {
-               DBG1(DBG_LIB, "getting file size of '%s' failed: %s", file,
-                        strerror(errno));
-               close(fd);
-               return;
-       }
-       addr = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
-       if (addr == MAP_FAILED)
-       {
-               DBG1(DBG_LIB, "mapping '%s' failed: %s", file, strerror(errno));
-               close(fd);
-               return;
-       }
-       src = chunk_create(addr, sb.st_size);
 
-       if (level == 0)
+       if (!secrets)
        {
-               this->lock->write_lock(this->lock);
-
-               /* flush secrets on non-recursive invocation */
-               while (this->shared->remove_last(this->shared,
-                                                                                (void**)&shared) == SUCCESS)
-               {
-                       shared->destroy(shared);
-               }
-               while (this->private->remove_last(this->private,
-                                                                                 (void**)&private) == SUCCESS)
-               {
-                       private->destroy(private);
-               }
+               secrets = mem_cred_create();
        }
 
-       while (fetchline(&src, &line))
+       while (fetchline(src, &line))
        {
                chunk_t ids, token;
                shared_key_type_t type;
@@ -1187,10 +1132,8 @@ static void load_secrets(private_stroke_cred_t *this, char *file, int level,
                {
                        continue;
                }
-               if (line.len > strlen("include ") &&
-                       strneq(line.ptr, "include ", strlen("include ")))
+               if (line.len > strlen("include ") && strpfx(line.ptr, "include "))
                {
-                       glob_t buf;
                        char **expanded, *dir, pattern[PATH_MAX];
                        u_char *pos;
 
@@ -1214,12 +1157,12 @@ static void load_secrets(private_stroke_cred_t *this, char *file, int level,
                                        DBG1(DBG_CFG, "include pattern too long, ignored");
                                        continue;
                                }
-                               snprintf(pattern, sizeof(pattern), "%.*s", line.len, line.ptr);
+                               snprintf(pattern, sizeof(pattern), "%.*s",
+                                                (int)line.len, line.ptr);
                        }
                        else
                        {       /* use directory of current file if relative */
-                               dir = strdup(file);
-                               dir = dirname(dir);
+                               dir = path_dirname(file);
 
                                if (line.len + 1 + strlen(dir) + 1 > sizeof(pattern))
                                {
@@ -1228,26 +1171,35 @@ static void load_secrets(private_stroke_cred_t *this, char *file, int level,
                                        continue;
                                }
                                snprintf(pattern, sizeof(pattern), "%s/%.*s",
-                                                dir, line.len, line.ptr);
+                                                dir, (int)line.len, line.ptr);
                                free(dir);
                        }
-                       if (glob(pattern, GLOB_ERR, NULL, &buf) != 0)
+#ifdef HAVE_GLOB_H
                        {
-                               DBG1(DBG_CFG, "expanding file expression '%s' failed", pattern);
-                               globfree(&buf);
-                       }
-                       else
-                       {
-                               for (expanded = buf.gl_pathv; *expanded != NULL; expanded++)
+                               glob_t buf;
+                               if (glob(pattern, GLOB_ERR, NULL, &buf) != 0)
                                {
-                                       load_secrets(this, *expanded, level + 1, prompt);
+                                       DBG1(DBG_CFG, "expanding file expression '%s' failed",
+                                                pattern);
                                }
+                               else
+                               {
+                                       for (expanded = buf.gl_pathv; *expanded != NULL; expanded++)
+                                       {
+                                               load_secrets(this, secrets, *expanded, level + 1,
+                                                                        prompt);
+                                       }
+                               }
+                               globfree(&buf);
                        }
-                       globfree(&buf);
+#else /* HAVE_GLOB_H */
+                       /* if glob(3) is not available, try to load pattern directly */
+                       load_secrets(this, secrets, pattern, level + 1, prompt);
+#endif /* HAVE_GLOB_H */
                        continue;
                }
 
-               if (line.len > 2 && strneq(": ", line.ptr, 2))
+               if (line.len > 2 && strpfx(line.ptr, ": "))
                {
                        /* no ids, skip the ':' */
                        ids = chunk_empty;
@@ -1272,15 +1224,22 @@ static void load_secrets(private_stroke_cred_t *this, char *file, int level,
                }
                if (match("RSA", &token) || match("ECDSA", &token))
                {
-                       if (!load_private(this, line, line_nr, prompt,
+                       if (!load_private(secrets, line, line_nr, prompt,
                                                          match("RSA", &token) ? KEY_RSA : KEY_ECDSA))
                        {
                                break;
                        }
                }
+               else if (match("P12", &token))
+               {
+                       if (!load_pkcs12(this, secrets, line, line_nr, prompt))
+                       {
+                               break;
+                       }
+               }
                else if (match("PIN", &token))
                {
-                       if (!load_pin(this, line, line_nr, prompt))
+                       if (!load_pin(secrets, line, line_nr, prompt))
                        {
                                break;
                        }
@@ -1290,7 +1249,7 @@ static void load_secrets(private_stroke_cred_t *this, char *file, int level,
                                 (match("NTLM", &token) && (type = SHARED_NT_HASH)) ||
                                 (match("XAUTH", &token) && (type = SHARED_EAP)))
                {
-                       if (!load_shared(this, line, line_nr, type, ids))
+                       if (!load_shared(secrets, line, line_nr, type, ids))
                        {
                                break;
                        }
@@ -1298,16 +1257,17 @@ static void load_secrets(private_stroke_cred_t *this, char *file, int level,
                else
                {
                        DBG1(DBG_CFG, "line %d: token must be either "
-                                "RSA, ECDSA, PSK, EAP, XAUTH or PIN", line_nr);
+                                "RSA, ECDSA, P12, PIN, PSK, EAP, XAUTH or NTLM", line_nr);
                        break;
                }
        }
+       chunk_unmap(src);
+
        if (level == 0)
-       {
-               this->lock->unlock(this->lock);
+       {       /* replace secrets in active credential set */
+               this->creds->replace_secrets(this->creds, secrets, FALSE);
+               secrets->destroy(secrets);
        }
-       munmap(addr, sb.st_size);
-       close(fd);
 }
 
 /**
@@ -1336,15 +1296,13 @@ static void load_certs(private_stroke_cred_t *this)
        load_certdir(this, CRL_DIR, CERT_X509_CRL, 0);
 }
 
-/**
- * Implementation of stroke_cred_t.reread.
- */
-static void reread(private_stroke_cred_t *this, stroke_msg_t *msg, FILE *prompt)
+METHOD(stroke_cred_t, reread, void,
+       private_stroke_cred_t *this, stroke_msg_t *msg, FILE *prompt)
 {
        if (msg->reread.flags & REREAD_SECRETS)
        {
                DBG1(DBG_CFG, "rereading secrets");
-               load_secrets(this, SECRETS_FILE, 0, prompt);
+               load_secrets(this, NULL, this->secrets_file, 0, prompt);
        }
        if (msg->reread.flags & REREAD_CACERTS)
        {
@@ -1379,15 +1337,17 @@ static void reread(private_stroke_cred_t *this, stroke_msg_t *msg, FILE *prompt)
        }
 }
 
-/**
- * Implementation of stroke_cred_t.destroy
- */
-static void destroy(private_stroke_cred_t *this)
+METHOD(stroke_cred_t, add_shared, void,
+       private_stroke_cred_t *this, shared_key_t *shared, linked_list_t *owners)
+{
+       this->creds->add_shared_list(this->creds, shared, owners);
+}
+
+METHOD(stroke_cred_t, destroy, void,
+       private_stroke_cred_t *this)
 {
-       this->certs->destroy_offset(this->certs, offsetof(certificate_t, destroy));
-       this->shared->destroy_offset(this->shared, offsetof(shared_key_t, destroy));
-       this->private->destroy_offset(this->private, offsetof(private_key_t, destroy));
-       this->lock->destroy(this->lock);
+       lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
+       this->creds->destroy(this->creds);
        free(this);
 }
 
@@ -1396,29 +1356,39 @@ static void destroy(private_stroke_cred_t *this)
  */
 stroke_cred_t *stroke_cred_create()
 {
-       private_stroke_cred_t *this = malloc_thing(private_stroke_cred_t);
-
-       this->public.set.create_private_enumerator = (void*)create_private_enumerator;
-       this->public.set.create_cert_enumerator = (void*)create_cert_enumerator;
-       this->public.set.create_shared_enumerator = (void*)create_shared_enumerator;
-       this->public.set.create_cdp_enumerator = (void*)return_null;
-       this->public.set.cache_cert = (void*)cache_cert;
-       this->public.reread = (void(*)(stroke_cred_t*, stroke_msg_t *msg, FILE*))reread;
-       this->public.load_ca = (certificate_t*(*)(stroke_cred_t*, char *filename))load_ca;
-       this->public.load_peer = (certificate_t*(*)(stroke_cred_t*, char *filename))load_peer;
-       this->public.cachecrl = (void(*)(stroke_cred_t*, bool enabled))cachecrl;
-       this->public.destroy = (void(*)(stroke_cred_t*))destroy;
-
-       this->certs = linked_list_create();
-       this->shared = linked_list_create();
-       this->private = linked_list_create();
-       this->lock = rwlock_create(RWLOCK_TYPE_DEFAULT);
+       private_stroke_cred_t *this;
 
-       load_certs(this);
-       load_secrets(this, SECRETS_FILE, 0, NULL);
+       INIT(this,
+               .public = {
+                       .set = {
+                               .create_private_enumerator = (void*)return_null,
+                               .create_cert_enumerator = (void*)return_null,
+                               .create_shared_enumerator = (void*)return_null,
+                               .create_cdp_enumerator = (void*)return_null,
+                               .cache_cert = (void*)_cache_cert,
+                       },
+                       .reread = _reread,
+                       .load_ca = _load_ca,
+                       .load_peer = _load_peer,
+                       .load_pubkey = _load_pubkey,
+                       .add_shared = _add_shared,
+                       .cachecrl = _cachecrl,
+                       .destroy = _destroy,
+               },
+               .secrets_file = lib->settings->get_str(lib->settings,
+                                                               "%s.plugins.stroke.secrets_file", SECRETS_FILE,
+                                                               lib->ns),
+               .creds = mem_cred_create(),
+       );
+
+       lib->credmgr->add_set(lib->credmgr, &this->creds->set);
+
+       this->force_ca_cert = lib->settings->get_bool(lib->settings,
+                                               "%s.plugins.stroke.ignore_missing_ca_basic_constraint",
+                                               FALSE, lib->ns);
 
-       this->cachecrl = FALSE;
+       load_certs(this);
+       load_secrets(this, NULL, this->secrets_file, 0, NULL);
 
        return &this->public;
 }
-