stroke: Allow specifying the ipsec.secrets location in strongswan.conf
[strongswan.git] / src / libcharon / plugins / stroke / stroke_cred.c
index c872ea9..83431d1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2012 Tobias Brunner
+ * Copyright (C) 2008-2013 Tobias Brunner
  * Copyright (C) 2008 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
@@ -17,8 +17,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <limits.h>
-#include <libgen.h>
-#include <sys/mman.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <unistd.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>
@@ -66,13 +65,18 @@ struct private_stroke_cred_t {
        stroke_cred_t public;
 
        /**
+        * secrets file with credential information
+        */
+       char *secrets_file;
+
+       /**
         * credentials
         */
        mem_cred_t *creds;
 
        /**
         * ignore missing CA basic constraint (i.e. treat all certificates in
-        * ipsec.conf ca sections and ipsec.d/cacert as CA certificates)
+        * ipsec.conf ca sections and ipsec.d/cacerts as CA certificates)
         */
        bool force_ca_cert;
 
@@ -82,6 +86,9 @@ struct private_stroke_cred_t {
        bool cachecrl;
 };
 
+/** Length of smartcard specifier parts (module, keyid) */
+#define SC_PART_LEN 128
+
 /**
  * Kind of smartcard specifier token
  */
@@ -96,16 +103,16 @@ typedef enum {
  * Parse a smartcard specifier token
  */
 static smartcard_format_t parse_smartcard(char *smartcard, u_int *slot,
-                                                                       char module[BUF_LEN], char keyid[BUF_LEN])
+                                                                                 char *module, char *keyid)
 {
        /* The token has one of the following three formats:
         * - %smartcard<slot>@<module>:<keyid>
         * - %smartcard<slot>:<keyid>
         * - %smartcard:<keyid>
         */
-       char buf[BUF_LEN], *pos;
+       char buf[2 * SC_PART_LEN], *pos;
 
-       if (sscanf(smartcard, "%%smartcard%u@%127s", slot, buf) == 2)
+       if (sscanf(smartcard, "%%smartcard%u@%255s", slot, buf) == 2)
        {
                pos = strchr(buf, ':');
                if (!pos)
@@ -113,15 +120,15 @@ static smartcard_format_t parse_smartcard(char *smartcard, u_int *slot,
                        return SC_FORMAT_INVALID;
                }
                *pos++ = '\0';
-               snprintf(module, BUF_LEN, "%s", buf);
-               snprintf(keyid, BUF_LEN, "%s", pos);
+               snprintf(module, SC_PART_LEN, "%s", buf);
+               snprintf(keyid, SC_PART_LEN, "%s", pos);
                return SC_FORMAT_SLOT_MODULE_KEYID;
        }
-       if (sscanf(smartcard, "%%smartcard%u:%63s", slot, keyid) == 2)
+       if (sscanf(smartcard, "%%smartcard%u:%127s", slot, keyid) == 2)
        {
                return SC_FORMAT_SLOT_KEYID;
        }
-       if (sscanf(smartcard, "%%smartcard:%63s", keyid) == 1)
+       if (sscanf(smartcard, "%%smartcard:%127s", keyid) == 1)
        {
                return SC_FORMAT_KEYID;
        }
@@ -171,10 +178,10 @@ METHOD(stroke_cred_t, load_ca, certificate_t*,
        certificate_t *cert = NULL;
        char path[PATH_MAX];
 
-       if (strneq(filename, "%smartcard", strlen("%smartcard")))
+       if (strpfx(filename, "%smartcard"))
        {
                smartcard_format_t format;
-               char module[BUF_LEN], keyid[BUF_LEN];
+               char module[SC_PART_LEN], keyid[SC_PART_LEN];
                u_int slot;
 
                format = parse_smartcard(filename, &slot, module, keyid);
@@ -222,7 +229,7 @@ METHOD(stroke_cred_t, load_ca, certificate_t*,
                        cert->destroy(cert);
                        return NULL;
                }
-               DBG1(DBG_CFG, "  loaded ca certificate \"%Y\" from '%s",
+               DBG1(DBG_CFG, "  loaded ca certificate \"%Y\" from '%s'",
                         cert->get_subject(cert), filename);
                return this->creds->add_cert_ref(this->creds, TRUE, cert);
        }
@@ -235,10 +242,10 @@ METHOD(stroke_cred_t, load_peer, certificate_t*,
        certificate_t *cert = NULL;
        char path[PATH_MAX];
 
-       if (strneq(filename, "%smartcard", strlen("%smartcard")))
+       if (strpfx(filename, "%smartcard"))
        {
                smartcard_format_t format;
-               char module[BUF_LEN], keyid[BUF_LEN];
+               char module[SC_PART_LEN], keyid[SC_PART_LEN];
                u_int slot;
 
                format = parse_smartcard(filename, &slot, module, keyid);
@@ -276,29 +283,45 @@ METHOD(stroke_cred_t, load_peer, certificate_t*,
 }
 
 METHOD(stroke_cred_t, load_pubkey, certificate_t*,
-       private_stroke_cred_t *this, key_type_t type, char *filename,
-       identification_t *identity)
+       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 (streq(filename, "%dns"))
        {
-
+               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, "0x", 2) || strncaseeq(filename, "0s", 2))
+       else if (strncaseeq(filename, "ssh:", 4))
+       {       /* SSH key */
+               build_part = BUILD_BLOB_SSHKEY;
+               filename += 4;
+       }
+       else
+       {       /* try PKCS#1 by default */
+               build_part = BUILD_BLOB_ASN1_DER;
+       }
+       if (strncaseeq(filename, "0x", 2) || strncaseeq(filename, "0s", 2))
        {
-               chunk_t printable_key, rfc3110_key;
-               public_key_t *key;
+               chunk_t printable_key, raw_key;
 
                printable_key = chunk_create(filename + 2, strlen(filename) - 2);
-               rfc3110_key = strncaseeq(filename, "0x", 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, KEY_RSA,
-                                                                BUILD_BLOB_DNSKEY, rfc3110_key,
-                                                                BUILD_END);
-               free(rfc3110_key.ptr);
+               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,
@@ -306,6 +329,7 @@ METHOD(stroke_cred_t, load_pubkey, certificate_t*,
                                                                          BUILD_PUBLIC_KEY, key,
                                                                          BUILD_SUBJECT, identity,
                                                                          BUILD_END);
+                       type = key->get_type(key);
                        key->destroy(key);
                        if (cert)
                        {
@@ -315,8 +339,7 @@ METHOD(stroke_cred_t, load_pubkey, certificate_t*,
                                return cert;
                        }
                }
-               DBG1(DBG_CFG, "  loading %N public key for \"%Y\" failed",
-                        key_type_names, type, identity);
+               DBG1(DBG_CFG, "  loading public key for \"%Y\" failed", identity);
        }
        else
        {
@@ -337,12 +360,15 @@ METHOD(stroke_cred_t, load_pubkey, certificate_t*,
                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 %N public key for \"%Y\" from '%s' failed",
-                        key_type_names, type, identity, filename);
+               DBG1(DBG_CFG, "  loading public key for \"%Y\" from '%s' failed",
+                        identity, filename);
        }
        return NULL;
 }
@@ -498,7 +524,16 @@ METHOD(stroke_cred_t, cache_cert, void,
 
                        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);
                        }
                }
@@ -577,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 */
@@ -586,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];
 
@@ -601,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))
        {
@@ -627,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;
@@ -667,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))
@@ -698,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[BUF_LEN], keyid[BUF_LEN], module[BUF_LEN];
+       char smartcard[BUF_LEN], keyid[SC_PART_LEN], module[SC_PART_LEN];
        private_key_t *key = NULL;
        u_int slot;
        chunk_t chunk;
@@ -749,7 +799,7 @@ 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)
@@ -761,7 +811,7 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
                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, FALSE);
        }
@@ -793,21 +843,20 @@ static bool load_pin(private_stroke_cred_t *this, chunk_t line, int line_nr,
        if (key)
        {
                DBG1(DBG_CFG, "  loaded private key from %.*s", (int)sc.len, sc.ptr);
-               this->creds->add_key(this->creds, key);
+               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);
 
@@ -824,12 +873,12 @@ 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", (int)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,
+               snprintf(path, PATH_MAX, "%s/%.*s", PRIVATE_KEY_DIR,
                                 (int)filename.len, filename.ptr);
        }
 
@@ -843,32 +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, FALSE);
 
-               key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, key_type,
-                                                                BUILD_FROM_FILE, path, BUILD_END);
+               *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
        {
@@ -879,19 +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);
+               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);
 
-               key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, key_type,
-                                                                BUILD_FROM_FILE, path, BUILD_END);
+               *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->creds->add_key(this->creds, key);
+               secrets->add_key(secrets, key);
        }
        else
        {
@@ -901,9 +985,61 @@ 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)
 {
        shared_key_t *shared_key;
@@ -958,51 +1094,34 @@ static bool load_shared(private_stroke_cred_t *this, chunk_t line, int line_nr,
                owners->insert_last(owners,
                                        identification_create_from_encoding(ID_ANY, chunk_empty));
        }
-       this->creds->add_shared_list(this->creds, shared_key, owners);
+       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;
-       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)
-       {       /* flush secrets on non-recursive invocation */
-               this->creds->clear_secrets(this->creds);
+       if (!secrets)
+       {
+               secrets = mem_cred_create();
        }
 
-       while (fetchline(&src, &line))
+       while (fetchline(src, &line))
        {
                chunk_t ids, token;
                shared_key_type_t type;
@@ -1013,8 +1132,7 @@ 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 "))
                {
                        char **expanded, *dir, pattern[PATH_MAX];
                        u_char *pos;
@@ -1044,8 +1162,7 @@ static void load_secrets(private_stroke_cred_t *this, char *file, int level,
                        }
                        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))
                                {
@@ -1069,19 +1186,20 @@ static void load_secrets(private_stroke_cred_t *this, char *file, int level,
                                {
                                        for (expanded = buf.gl_pathv; *expanded != NULL; expanded++)
                                        {
-                                               load_secrets(this, *expanded, level + 1, prompt);
+                                               load_secrets(this, secrets, *expanded, level + 1,
+                                                                        prompt);
                                        }
                                }
                                globfree(&buf);
                        }
 #else /* HAVE_GLOB_H */
                        /* if glob(3) is not available, try to load pattern directly */
-                       load_secrets(this, pattern, level + 1, prompt);
+                       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;
@@ -1106,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;
                        }
@@ -1124,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;
                        }
@@ -1132,12 +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;
                }
        }
-       munmap(addr, sb.st_size);
-       close(fd);
+       chunk_unmap(src);
+
+       if (level == 0)
+       {       /* replace secrets in active credential set */
+               this->creds->replace_secrets(this->creds, secrets, FALSE);
+               secrets->destroy(secrets);
+       }
 }
 
 /**
@@ -1172,7 +1302,7 @@ METHOD(stroke_cred_t, reread, void,
        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)
        {
@@ -1245,6 +1375,9 @@ stroke_cred_t *stroke_cred_create()
                        .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(),
        );
 
@@ -1252,11 +1385,10 @@ stroke_cred_t *stroke_cred_create()
 
        this->force_ca_cert = lib->settings->get_bool(lib->settings,
                                                "%s.plugins.stroke.ignore_missing_ca_basic_constraint",
-                                               FALSE, charon->name);
+                                               FALSE, lib->ns);
 
        load_certs(this);
-       load_secrets(this, SECRETS_FILE, 0, NULL);
+       load_secrets(this, NULL, this->secrets_file, 0, NULL);
 
        return &this->public;
 }
-