child-rekey: Don't change state to INSTALLED if it was already REKEYING
[strongswan.git] / src / libstrongswan / credentials / credential_manager.c
index c5a6816..95c5cd7 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2015 Tobias Brunner
  * Copyright (C) 2007 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
 #include "credential_manager.h"
 
 #include <library.h>
-#include <debug.h>
+#include <utils/debug.h>
 #include <threading/thread_value.h>
 #include <threading/mutex.h>
 #include <threading/rwlock.h>
-#include <selectors/traffic_selector.h>
-#include <utils/linked_list.h>
+#include <collections/linked_list.h>
 #include <credentials/sets/cert_cache.h>
 #include <credentials/sets/auth_cfg_wrapper.h>
-#include <credentials/sets/ocsp_response_wrapper.h>
 #include <credentials/certificates/x509.h>
-#include <credentials/certificates/crl.h>
-#include <credentials/certificates/ocsp_request.h>
-#include <credentials/certificates/ocsp_response.h>
 
 /**
  * Maximum length of a certificate trust chain
@@ -58,6 +54,11 @@ struct private_credential_manager_t {
        thread_value_t *local_sets;
 
        /**
+        * Exclusive local sets, linked_list_t with credential_set_t
+        */
+       thread_value_t *exclusive_local_sets;
+
+       /**
         * trust relationship and certificate cache
         */
        cert_cache_t *cache;
@@ -81,6 +82,16 @@ struct private_credential_manager_t {
         * mutex for cache queue
         */
        mutex_t *queue_mutex;
+
+       /**
+        * Registered hook to call on validation errors
+        */
+       credential_hook_t hook;
+
+       /**
+        * Registered data to pass to hook
+        */
+       void *hook_data;
 };
 
 /** data to pass to create_private_enumerator */
@@ -122,12 +133,39 @@ typedef struct {
        enumerator_t *global;
        /** enumerator over local sets */
        enumerator_t *local;
+       /** enumerator over exclusive local sets */
+       enumerator_t *exclusive;
 } sets_enumerator_t;
 
+METHOD(credential_manager_t, set_hook, void,
+       private_credential_manager_t *this, credential_hook_t hook, void *data)
+{
+       this->hook = hook;
+       this->hook_data = data;
+}
+
+METHOD(credential_manager_t, call_hook, void,
+       private_credential_manager_t *this, credential_hook_type_t type,
+       certificate_t *cert)
+{
+       if (this->hook)
+       {
+               this->hook(this->hook_data, type, cert);
+       }
+}
 
 METHOD(enumerator_t, sets_enumerate, bool,
        sets_enumerator_t *this, credential_set_t **set)
 {
+       if (this->exclusive)
+       {
+               if (this->exclusive->enumerate(this->exclusive, set))
+               {       /* only enumerate last added */
+                       this->exclusive->destroy(this->exclusive);
+                       this->exclusive = NULL;
+                       return TRUE;
+               }
+       }
        if (this->global)
        {
                if (this->global->enumerate(this->global, set))
@@ -150,6 +188,7 @@ METHOD(enumerator_t, sets_destroy, void,
 {
        DESTROY_IF(this->global);
        DESTROY_IF(this->local);
+       DESTROY_IF(this->exclusive);
        free(this);
 }
 
@@ -159,17 +198,28 @@ METHOD(enumerator_t, sets_destroy, void,
 static enumerator_t *create_sets_enumerator(private_credential_manager_t *this)
 {
        sets_enumerator_t *enumerator;
-       linked_list_t *local;
+       linked_list_t *list;
 
        INIT(enumerator,
-               .public.enumerate = (void*)_sets_enumerate,
-               .public.destroy = _sets_destroy,
-               .global = this->sets->create_enumerator(this->sets),
+               .public = {
+                       .enumerate = (void*)_sets_enumerate,
+                       .destroy = _sets_destroy,
+               },
        );
-       local = this->local_sets->get(this->local_sets);
-       if (local)
+
+       list = this->exclusive_local_sets->get(this->exclusive_local_sets);
+       if (list && list->get_count(list))
+       {
+               enumerator->exclusive = list->create_enumerator(list);
+       }
+       else
        {
-               enumerator->local = local->create_enumerator(local);
+               enumerator->global = this->sets->create_enumerator(this->sets);
+               list = this->local_sets->get(this->local_sets);
+               if (list)
+               {
+                       enumerator->local = list->create_enumerator(list);
+               }
        }
        return &enumerator->public;
 }
@@ -355,8 +405,8 @@ METHOD(credential_manager_t, get_shared, shared_key_t*,
        identification_t *me, identification_t *other)
 {
        shared_key_t *current, *found = NULL;
-       id_match_t *best_me = ID_MATCH_NONE, *best_other = ID_MATCH_NONE;
-       id_match_t *match_me, *match_other;
+       id_match_t best_me = ID_MATCH_NONE, best_other = ID_MATCH_NONE;
+       id_match_t match_me, match_other;
        enumerator_t *enumerator;
 
        enumerator = create_shared_enumerator(this, type, me, other);
@@ -370,38 +420,76 @@ METHOD(credential_manager_t, get_shared, shared_key_t*,
                        best_me = match_me;
                        best_other = match_other;
                }
+               if (best_me == ID_MATCH_PERFECT && best_other == ID_MATCH_PERFECT)
+               {
+                       break;
+               }
        }
        enumerator->destroy(enumerator);
        return found;
 }
 
-/**
- * add a credential set to the thread local list
- */
-static void add_local_set(private_credential_manager_t *this,
-                                                 credential_set_t *set)
+METHOD(credential_manager_t, add_local_set, void,
+       private_credential_manager_t *this, credential_set_t *set, bool exclusive)
 {
        linked_list_t *sets;
+       thread_value_t *tv;
 
-       sets = this->local_sets->get(this->local_sets);
+       if (exclusive)
+       {
+               tv = this->exclusive_local_sets;
+       }
+       else
+       {
+               tv = this->local_sets;
+       }
+       sets = tv->get(tv);
        if (!sets)
-       {       /* first invocation */
+       {
                sets = linked_list_create();
-               this->local_sets->set(this->local_sets, sets);
+               tv->set(tv, sets);
+       }
+       if (exclusive)
+       {
+               sets->insert_first(sets, set);
+       }
+       else
+       {
+               sets->insert_last(sets, set);
        }
-       sets->insert_last(sets, set);
 }
 
-/**
- * remove a credential set from the thread local list
- */
-static void remove_local_set(private_credential_manager_t *this,
-                                                        credential_set_t *set)
+METHOD(credential_manager_t, remove_local_set, void,
+       private_credential_manager_t *this, credential_set_t *set)
 {
        linked_list_t *sets;
+       thread_value_t *tv;
+
+       tv = this->local_sets;
+       sets = tv->get(tv);
+       if (sets && sets->remove(sets, set, NULL) && sets->get_count(sets) == 0)
+       {
+               tv->set(tv, NULL);
+               sets->destroy(sets);
+       }
+       tv = this->exclusive_local_sets;
+       sets = tv->get(tv);
+       if (sets && sets->remove(sets, set, NULL) && sets->get_count(sets) == 0)
+       {
+               tv->set(tv, NULL);
+               sets->destroy(sets);
+       }
+}
 
-       sets = this->local_sets->get(this->local_sets);
-       sets->remove(sets, set, NULL);
+METHOD(credential_manager_t, issued_by, bool,
+       private_credential_manager_t *this, certificate_t *subject,
+       certificate_t *issuer, signature_scheme_t *scheme)
+{
+       if (this->cache)
+       {
+               return this->cache->issued_by(this->cache, subject, issuer, scheme);
+       }
+       return subject->issued_by(subject, issuer, scheme);
 }
 
 METHOD(credential_manager_t, cache_cert, void,
@@ -458,636 +546,78 @@ static void cache_queue(private_credential_manager_t *this)
 }
 
 /**
- * forward declaration
- */
-static enumerator_t *create_trusted_enumerator(private_credential_manager_t *this,
-                                       key_type_t type, identification_t *id, bool online);
-
-/**
- * Do an OCSP request
- */
-static certificate_t *fetch_ocsp(private_credential_manager_t *this, char *url,
-                                                                certificate_t *subject, certificate_t *issuer)
-{
-       certificate_t *request, *response;
-       chunk_t send, receive;
-
-       /* TODO: requestor name, signature */
-       request = lib->creds->create(lib->creds,
-                                               CRED_CERTIFICATE, CERT_X509_OCSP_REQUEST,
-                                               BUILD_CA_CERT, issuer,
-                                               BUILD_CERT, subject, BUILD_END);
-       if (!request)
-       {
-               DBG1(DBG_CFG, "generating ocsp request failed");
-               return NULL;
-       }
-
-       send = request->get_encoding(request);
-       request->destroy(request);
-
-       DBG1(DBG_CFG, "  requesting ocsp status from '%s' ...", url);
-       if (lib->fetcher->fetch(lib->fetcher, url, &receive,
-                                                       FETCH_REQUEST_DATA, send,
-                                                       FETCH_REQUEST_TYPE, "application/ocsp-request",
-                                                       FETCH_END) != SUCCESS)
-       {
-               DBG1(DBG_CFG, "ocsp request to %s failed", url);
-               chunk_free(&send);
-               return NULL;
-       }
-       chunk_free(&send);
-
-       response = lib->creds->create(lib->creds,
-                                                                 CRED_CERTIFICATE, CERT_X509_OCSP_RESPONSE,
-                                                                 BUILD_BLOB_ASN1_DER, receive, BUILD_END);
-       chunk_free(&receive);
-       if (!response)
-       {
-               DBG1(DBG_CFG, "parsing ocsp response failed");
-               return NULL;
-       }
-       return response;
-}
-
-/**
- * check the signature of an OCSP response
- */
-static bool verify_ocsp(private_credential_manager_t *this,
-                                               ocsp_response_t *response)
-{
-       certificate_t *issuer, *subject;
-       identification_t *responder;
-       ocsp_response_wrapper_t *wrapper;
-       enumerator_t *enumerator;
-       bool verified = FALSE;
-
-       wrapper = ocsp_response_wrapper_create((ocsp_response_t*)response);
-       add_local_set(this, &wrapper->set);
-
-       subject = &response->certificate;
-       responder = subject->get_issuer(subject);
-       enumerator = create_trusted_enumerator(this, KEY_ANY, responder, FALSE);
-       while (enumerator->enumerate(enumerator, &issuer, NULL))
-       {
-               if (this->cache->issued_by(this->cache, subject, issuer))
-               {
-                       DBG1(DBG_CFG, "  ocsp response correctly signed by \"%Y\"",
-                                                        issuer->get_subject(issuer));
-                       verified = TRUE;
-                       break;
-               }
-       }
-       enumerator->destroy(enumerator);
-
-       remove_local_set(this, &wrapper->set);
-       wrapper->destroy(wrapper);
-       return verified;
-}
-
-/**
- * Get the better of two OCSP responses, and check for usable OCSP info
- */
-static certificate_t *get_better_ocsp(private_credential_manager_t *this,
-                                                                         certificate_t *cand, certificate_t *best,
-                                                                         x509_t *subject, x509_t *issuer,
-                                                                         cert_validation_t *valid, bool cache)
-{
-       ocsp_response_t *response;
-       time_t revocation, this_update, next_update, valid_until;
-       crl_reason_t reason;
-       bool revoked = FALSE;
-
-       response = (ocsp_response_t*)cand;
-
-       /* check ocsp signature */
-       if (!verify_ocsp(this, response))
-       {
-               DBG1(DBG_CFG, "ocsp response verification failed");
-               cand->destroy(cand);
-               return best;
-       }
-       /* check if response contains our certificate */
-       switch (response->get_status(response, subject, issuer, &revocation, &reason,
-                                                                &this_update, &next_update))
-       {
-               case VALIDATION_REVOKED:
-                       /* subject has been revoked by a valid OCSP response */
-                       DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
-                                                 &revocation, TRUE, crl_reason_names, reason);
-                       revoked = TRUE;
-                       break;
-               case VALIDATION_GOOD:
-                       /* results in either good or stale */
-                       break;
-               default:
-               case VALIDATION_FAILED:
-                       /* candidate unusable, does not contain our cert */
-                       DBG1(DBG_CFG, "  ocsp response contains no status on our certificate");
-                       cand->destroy(cand);
-                       return best;
-       }
-
-       /* select the better of the two responses */
-       if (best == NULL || certificate_is_newer(cand, best))
-       {
-               DESTROY_IF(best);
-               best = cand;
-               if (best->get_validity(best, NULL, NULL, &valid_until))
-               {
-                       DBG1(DBG_CFG, "  ocsp response is valid: until %T",
-                                                        &valid_until, FALSE);
-                       *valid = VALIDATION_GOOD;
-                       if (cache)
-                       {       /* cache non-stale only, stale certs get refetched */
-                               cache_cert(this, best);
-                       }
-               }
-               else
-               {
-                       DBG1(DBG_CFG, "  ocsp response is stale: since %T",
-                                                        &valid_until, FALSE);
-                       *valid = VALIDATION_STALE;
-               }
-       }
-       else
-       {
-               *valid = VALIDATION_STALE;
-               cand->destroy(cand);
-       }
-       if (revoked)
-       {       /* revoked always counts, even if stale */
-               *valid = VALIDATION_REVOKED;
-       }
-       return best;
-}
-
-/**
- * validate a x509 certificate using OCSP
+ * Use validators to check the lifetime of certificates
  */
-static cert_validation_t check_ocsp(private_credential_manager_t *this,
-                                                                       x509_t *subject, x509_t *issuer,
-                                                                       auth_cfg_t *auth)
+static bool check_lifetime(private_credential_manager_t *this,
+                                                  certificate_t *cert, char *label,
+                                                  int pathlen, bool trusted, auth_cfg_t *auth)
 {
+       time_t not_before, not_after;
+       cert_validator_t *validator;
        enumerator_t *enumerator;
-       cert_validation_t valid = VALIDATION_SKIPPED;
-       certificate_t *best = NULL, *current;
-       identification_t *keyid = NULL;
-       public_key_t *public;
-       chunk_t chunk;
-       char *uri = NULL;
-
-       /** lookup cache for valid OCSP responses */
-       enumerator = create_cert_enumerator(this, CERT_X509_OCSP_RESPONSE,
-                                                                               KEY_ANY, NULL, FALSE);
-       while (enumerator->enumerate(enumerator, &current))
-       {
-               current->get_ref(current);
-               best = get_better_ocsp(this, current, best, subject, issuer,
-                                                          &valid, FALSE);
-               if (best && valid != VALIDATION_STALE)
-               {
-                       DBG1(DBG_CFG, "  using cached ocsp response");
-                       break;
-               }
-       }
-       enumerator->destroy(enumerator);
-
-       /* derive the authorityKeyIdentifier from the issuer's public key */
-       current = &issuer->interface;
-       public = current->get_public_key(current);
-       if (public && public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
-       {
-               keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
-       }
-       /** fetch from configured OCSP responder URLs */
-       if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
-       {
-               enumerator = create_cdp_enumerator(this, CERT_X509_OCSP_RESPONSE, keyid);
-               while (enumerator->enumerate(enumerator, &uri))
-               {
-                       current = fetch_ocsp(this, uri, &subject->interface,
-                                                                &issuer->interface);
-                       if (current)
-                       {
-                               best = get_better_ocsp(this, current, best, subject, issuer,
-                                                                          &valid, TRUE);
-                               if (best && valid != VALIDATION_STALE)
-                               {
-                                       break;
-                               }
-                       }
-               }
-               enumerator->destroy(enumerator);
-       }
-       DESTROY_IF(public);
-       DESTROY_IF(keyid);
+       status_t status = NEED_MORE;
 
-       /* fallback to URL fetching from subject certificate's URIs */
-       if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
+       enumerator = this->validators->create_enumerator(this->validators);
+       while (enumerator->enumerate(enumerator, &validator))
        {
-               enumerator = subject->create_ocsp_uri_enumerator(subject);
-               while (enumerator->enumerate(enumerator, &uri))
+               if (!validator->check_lifetime)
                {
-                       current = fetch_ocsp(this, uri, &subject->interface,
-                                                                &issuer->interface);
-                       if (current)
-                       {
-                               best = get_better_ocsp(this, current, best, subject, issuer,
-                                                                          &valid, TRUE);
-                               if (best && valid != VALIDATION_STALE)
-                               {
-                                       break;
-                               }
-                       }
-               }
-               enumerator->destroy(enumerator);
-       }
-       /* an uri was found, but no result. switch validation state to failed */
-       if (valid == VALIDATION_SKIPPED && uri)
-       {
-               valid = VALIDATION_FAILED;
-       }
-       if (auth)
-       {
-               auth->add(auth, AUTH_RULE_OCSP_VALIDATION, valid);
-               if (valid == VALIDATION_GOOD)
-               {       /* successful OCSP check fulfills also CRL constraint */
-                       auth->add(auth, AUTH_RULE_CRL_VALIDATION, VALIDATION_GOOD);
+                       continue;
                }
-       }
-       DESTROY_IF(best);
-       return valid;
-}
-
-/**
- * fetch a CRL from an URL
- */
-static certificate_t* fetch_crl(private_credential_manager_t *this, char *url)
-{
-       certificate_t *crl;
-       chunk_t chunk;
-
-       DBG1(DBG_CFG, "  fetching crl from '%s' ...", url);
-       if (lib->fetcher->fetch(lib->fetcher, url, &chunk, FETCH_END) != SUCCESS)
-       {
-               DBG1(DBG_CFG, "crl fetching failed");
-               return NULL;
-       }
-       crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
-                                                        BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
-       chunk_free(&chunk);
-       if (!crl)
-       {
-               DBG1(DBG_CFG, "crl fetched successfully but parsing failed");
-               return NULL;
-       }
-       return crl;
-}
-
-/**
- * check the signature of an CRL
- */
-static bool verify_crl(private_credential_manager_t *this, certificate_t *crl)
-{
-       certificate_t *issuer;
-       enumerator_t *enumerator;
-       bool verified = FALSE;
-
-       enumerator = create_trusted_enumerator(this, KEY_ANY, crl->get_issuer(crl),
-                                                                                  FALSE);
-       while (enumerator->enumerate(enumerator, &issuer, NULL))
-       {
-               if (this->cache->issued_by(this->cache, crl, issuer))
+               status = validator->check_lifetime(validator, cert,
+                                                                                  pathlen, trusted, auth);
+               if (status != NEED_MORE)
                {
-                       DBG1(DBG_CFG, "  crl correctly signed by \"%Y\"",
-                                                  issuer->get_subject(issuer));
-                       verified = TRUE;
                        break;
                }
        }
        enumerator->destroy(enumerator);
 
-       return verified;
-}
-
-/**
- * Get the better of two CRLs, and check for usable CRL info
- */
-static certificate_t *get_better_crl(private_credential_manager_t *this,
-                                                                        certificate_t *cand, certificate_t *best,
-                                                                        x509_t *subject, x509_t *issuer,
-                                                                        cert_validation_t *valid, bool cache)
-{
-       enumerator_t *enumerator;
-       time_t revocation, valid_until;
-       crl_reason_t reason;
-       chunk_t serial;
-       crl_t *crl;
-
-       /* check CRL signature */
-       if (!verify_crl(this, cand))
-       {
-               DBG1(DBG_CFG, "crl response verification failed");
-               cand->destroy(cand);
-               return best;
-       }
-
-       crl = (crl_t*)cand;
-       enumerator = crl->create_enumerator(crl);
-       while (enumerator->enumerate(enumerator, &serial, &revocation, &reason))
-       {
-               if (chunk_equals(serial, subject->get_serial(subject)))
-               {
-                       DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
-                                &revocation, TRUE, crl_reason_names, reason);
-                       *valid = VALIDATION_REVOKED;
-                       enumerator->destroy(enumerator);
-                       DESTROY_IF(best);
-                       return cand;
-               }
-       }
-       enumerator->destroy(enumerator);
-
-       /* select the better of the two CRLs */
-       if (best == NULL || crl_is_newer(crl, (crl_t*)best))
-       {
-               DESTROY_IF(best);
-               best = cand;
-               if (best->get_validity(best, NULL, NULL, &valid_until))
-               {
-                       DBG1(DBG_CFG, "  crl is valid: until %T", &valid_until, FALSE);
-                       *valid = VALIDATION_GOOD;
-                       if (cache)
-                       {       /* we cache non-stale crls only, as a stale crls are refetched */
-                               cache_cert(this, best);
-                       }
-               }
-               else
-               {
-                       DBG1(DBG_CFG, "  crl is stale: since %T", &valid_until, FALSE);
-                       *valid = VALIDATION_STALE;
-               }
-       }
-       else
-       {
-               *valid = VALIDATION_STALE;
-               cand->destroy(cand);
-       }
-       return best;
-}
-
-/**
- * validate a x509 certificate using CRL
- */
-static cert_validation_t check_crl(private_credential_manager_t *this,
-                                                                  x509_t *subject, x509_t *issuer,
-                                                                  auth_cfg_t *auth)
-{
-       cert_validation_t valid = VALIDATION_SKIPPED;
-       identification_t *keyid = NULL;
-       certificate_t *best = NULL;
-       certificate_t *current;
-       public_key_t *public;
-       enumerator_t *enumerator;
-       chunk_t chunk;
-       char *uri = NULL;
-
-       /* derive the authorityKeyIdentifier from the issuer's public key */
-       current = &issuer->interface;
-       public = current->get_public_key(current);
-       if (public && public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
-       {
-               keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
-
-               /* find a cached crl by authorityKeyIdentifier */
-               enumerator = create_cert_enumerator(this, CERT_X509_CRL, KEY_ANY,
-                                                                                       keyid, FALSE);
-               while (enumerator->enumerate(enumerator, &current))
-               {
-                       current->get_ref(current);
-                       best = get_better_crl(this, current, best, subject, issuer,
-                                                                 &valid, FALSE);
-                       if (best && valid != VALIDATION_STALE)
-                       {
-                               DBG1(DBG_CFG, "  using cached crl");
-                               break;
-                       }
-               }
-               enumerator->destroy(enumerator);
-
-               /* fallback to fetching crls from credential sets cdps */
-               if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
-               {
-                       enumerator = create_cdp_enumerator(this, CERT_X509_CRL, keyid);
-
-                       while (enumerator->enumerate(enumerator, &uri))
-                       {
-                               current = fetch_crl(this, uri);
-                               if (current)
-                               {
-                                       best = get_better_crl(this, current, best, subject, issuer,
-                                                                                 &valid, TRUE);
-                                       if (best && valid != VALIDATION_STALE)
-                                       {
-                                               break;
-                                       }
-                               }
-                       }
-                       enumerator->destroy(enumerator);
-               }
-               keyid->destroy(keyid);
-       }
-       DESTROY_IF(public);
-
-       /* fallback to fetching crls from cdps from subject's certificate */
-       if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
-       {
-               enumerator = subject->create_crl_uri_enumerator(subject);
-
-               while (enumerator->enumerate(enumerator, &uri))
-               {
-                       current = fetch_crl(this, uri);
-                       if (current)
-                       {
-                               best = get_better_crl(this, current, best, subject, issuer,
-                                                                         &valid, TRUE);
-                               if (best && valid != VALIDATION_STALE)
-                               {
-                                       break;
-                               }
-                       }
-               }
-               enumerator->destroy(enumerator);
-       }
-
-       /* an uri was found, but no result. switch validation state to failed */
-       if (valid == VALIDATION_SKIPPED && uri)
-       {
-               valid = VALIDATION_FAILED;
-       }
-       if (auth)
-       {
-               if (valid == VALIDATION_SKIPPED)
-               {       /* if we skipped CRL validation, we use the result of OCSP for
-                        * constraint checking */
-                       auth->add(auth, AUTH_RULE_CRL_VALIDATION,
-                                         auth->get(auth, AUTH_RULE_OCSP_VALIDATION));
-               }
-               else
-               {
-                       auth->add(auth, AUTH_RULE_CRL_VALIDATION, valid);
-               }
-       }
-       DESTROY_IF(best);
-       return valid;
-}
-
-/**
- * check a certificate for optional IP address block constraints
- */
-static bool check_ip_addr_block_constraints(x509_t *subject, x509_t *issuer)
-{
-       bool subject_constraint = subject->get_flags(subject) & X509_IP_ADDR_BLOCKS;
-       bool issuer_constraint = issuer->get_flags(issuer) & X509_IP_ADDR_BLOCKS;
-       bool contained = TRUE;
-
-       enumerator_t *subject_enumerator, *issuer_enumerator;
-       traffic_selector_t *subject_ts, *issuer_ts;
-
-       if (!subject_constraint && !issuer_constraint)
-       {
-               return TRUE;
-       }
-       if (!subject_constraint)
-       {
-               DBG1(DBG_CFG, "subject certficate lacks ipAddrBlocks extension");
-               return FALSE;
-       }
-       if (!issuer_constraint)
+       switch (status)
        {
-               DBG1(DBG_CFG, "issuer certficate lacks ipAddrBlocks extension");
-               return FALSE;
-       }
-       subject_enumerator = subject->create_ipAddrBlock_enumerator(subject);
-       while (subject_enumerator->enumerate(subject_enumerator, &subject_ts))
-       {
-               contained = FALSE;
-
-               issuer_enumerator = issuer->create_ipAddrBlock_enumerator(issuer);
-               while (issuer_enumerator->enumerate(issuer_enumerator, &issuer_ts))
-               {
-                       if (subject_ts->is_contained_in(subject_ts, issuer_ts))
+               case NEED_MORE:
+                       if (!cert->get_validity(cert, NULL, &not_before, &not_after))
                        {
-                               DBG2(DBG_CFG, "  subject address block %R is contained in "
-                                                         "issuer address block %R", subject_ts, issuer_ts);
-                               contained = TRUE;
+                               DBG1(DBG_CFG, "%s certificate invalid (valid from %T to %T)",
+                                        label, &not_before, FALSE, &not_after, FALSE);
                                break;
                        }
-               }
-               issuer_enumerator->destroy(issuer_enumerator);
-               if (!contained)
-               {
-                       DBG1(DBG_CFG, "subject address block %R is not contained in any "
-                                                 "issuer address block", subject_ts);
+                       return TRUE;
+               case SUCCESS:
+                       return TRUE;
+               case FAILED:
+               default:
                        break;
-               }
        }
-       subject_enumerator->destroy(subject_enumerator);
-       return contained;
+       call_hook(this, CRED_HOOK_EXPIRED, cert);
+       return FALSE;
 }
 
 /**
  * check a certificate for its lifetime
  */
 static bool check_certificate(private_credential_manager_t *this,
-                                                         certificate_t *subject, certificate_t *issuer,
-                                                         bool online, int pathlen, auth_cfg_t *auth)
+                               certificate_t *subject, certificate_t *issuer, bool online,
+                               int pathlen, bool trusted, auth_cfg_t *auth)
 {
-       time_t not_before, not_after;
        cert_validator_t *validator;
        enumerator_t *enumerator;
 
-       if (!subject->get_validity(subject, NULL, &not_before, &not_after))
+       if (!check_lifetime(this, subject, "subject", pathlen, FALSE, auth) ||
+               !check_lifetime(this, issuer, "issuer", pathlen + 1, trusted, auth))
        {
-               DBG1(DBG_CFG, "subject certificate invalid (valid from %T to %T)",
-                        &not_before, FALSE, &not_after, FALSE);
                return FALSE;
        }
-       if (!issuer->get_validity(issuer, NULL, &not_before, &not_after))
-       {
-               DBG1(DBG_CFG, "issuer certificate invalid (valid from %T to %T)",
-                        &not_before, FALSE, &not_after, FALSE);
-               return FALSE;
-       }
-       if (issuer->get_type(issuer) == CERT_X509 &&
-               subject->get_type(subject) == CERT_X509)
-       {
-               int pathlen_constraint;
-               x509_t *x509;
-
-               if (!check_ip_addr_block_constraints((x509_t*)subject, (x509_t*)issuer))
-               {
-                       return FALSE;
-               }
-
-               /* check path length constraint */
-               x509 = (x509_t*)issuer;
-               pathlen_constraint = x509->get_pathLenConstraint(x509);
-               if (pathlen_constraint != X509_NO_PATH_LEN_CONSTRAINT &&
-                       pathlen > pathlen_constraint)
-               {
-                       DBG1(DBG_CFG, "path length of %d violates constraint of %d",
-                                pathlen, pathlen_constraint);
-                       return FALSE;
-               }
-
-               if (online)
-               {
-                       DBG1(DBG_CFG, "checking certificate status of \"%Y\"",
-                                                  subject->get_subject(subject));
-                       switch (check_ocsp(this, (x509_t*)subject, (x509_t*)issuer, auth))
-                       {
-                               case VALIDATION_GOOD:
-                                       DBG1(DBG_CFG, "certificate status is good");
-                                       return TRUE;
-                               case VALIDATION_REVOKED:
-                                       /* has already been logged */
-                                       return FALSE;
-                               case VALIDATION_SKIPPED:
-                                       DBG2(DBG_CFG, "ocsp check skipped, no ocsp found");
-                                       break;
-                               case VALIDATION_STALE:
-                                       DBG1(DBG_CFG, "ocsp information stale, fallback to crl");
-                                       break;
-                               case VALIDATION_FAILED:
-                                       DBG1(DBG_CFG, "ocsp check failed, fallback to crl");
-                                       break;
-                       }
-                       switch (check_crl(this, (x509_t*)subject, (x509_t*)issuer, auth))
-                       {
-                               case VALIDATION_GOOD:
-                                       DBG1(DBG_CFG, "certificate status is good");
-                                       return TRUE;
-                               case VALIDATION_REVOKED:
-                                       /* has already been logged */
-                                       return FALSE;
-                               case VALIDATION_FAILED:
-                               case VALIDATION_SKIPPED:
-                                       DBG1(DBG_CFG, "certificate status is not available");
-                                       break;
-                               case VALIDATION_STALE:
-                                       DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
-                                       break;
-                       }
-               }
-       }
 
        enumerator = this->validators->create_enumerator(this->validators);
        while (enumerator->enumerate(enumerator, &validator))
        {
+               if (!validator->validate)
+               {
+                       continue;
+               }
                if (!validator->validate(validator, subject, issuer,
-                                                                online, pathlen, auth))
+                                                                online, pathlen, trusted, auth))
                {
                        enumerator->destroy(enumerator);
                        return FALSE;
@@ -1125,7 +655,8 @@ static certificate_t *get_pretrusted_cert(private_credential_manager_t *this,
  * Get the issuing certificate of a subject certificate
  */
 static certificate_t *get_issuer_cert(private_credential_manager_t *this,
-                                                                         certificate_t *subject, bool trusted)
+                                                                         certificate_t *subject, bool trusted,
+                                                                         signature_scheme_t *scheme)
 {
        enumerator_t *enumerator;
        certificate_t *issuer = NULL, *candidate;
@@ -1134,7 +665,7 @@ static certificate_t *get_issuer_cert(private_credential_manager_t *this,
                                                                                subject->get_issuer(subject), trusted);
        while (enumerator->enumerate(enumerator, &candidate))
        {
-               if (this->cache->issued_by(this->cache, subject, candidate))
+               if (issued_by(this, subject, candidate, scheme))
                {
                        issuer = candidate->get_ref(candidate);
                        break;
@@ -1145,6 +676,40 @@ static certificate_t *get_issuer_cert(private_credential_manager_t *this,
 }
 
 /**
+ * Get the strength of certificate, add it to auth
+ */
+static void get_key_strength(certificate_t *cert, auth_cfg_t *auth)
+{
+       uintptr_t strength;
+       public_key_t *key;
+       key_type_t type;
+
+       key = cert->get_public_key(cert);
+       if (key)
+       {
+               type = key->get_type(key);
+               strength = key->get_keysize(key);
+               DBG2(DBG_CFG, "  certificate \"%Y\" key: %d bit %N",
+                        cert->get_subject(cert), strength, key_type_names, type);
+               switch (type)
+               {
+                       case KEY_RSA:
+                               auth->add(auth, AUTH_RULE_RSA_STRENGTH, strength);
+                               break;
+                       case KEY_ECDSA:
+                               auth->add(auth, AUTH_RULE_ECDSA_STRENGTH, strength);
+                               break;
+                       case KEY_BLISS:
+                               auth->add(auth, AUTH_RULE_BLISS_STRENGTH, strength);
+                               break;
+                       default:
+                               break;
+               }
+               key->destroy(key);
+       }
+}
+
+/**
  * try to verify the trust chain of subject, return TRUE if trusted
  */
 static bool verify_trust_chain(private_credential_manager_t *this,
@@ -1153,18 +718,21 @@ static bool verify_trust_chain(private_credential_manager_t *this,
 {
        certificate_t *current, *issuer;
        auth_cfg_t *auth;
+       signature_scheme_t scheme;
        int pathlen;
 
        auth = auth_cfg_create();
+       get_key_strength(subject, auth);
        current = subject->get_ref(subject);
+       auth->add(auth, AUTH_RULE_SUBJECT_CERT, current->get_ref(current));
 
        for (pathlen = 0; pathlen <= MAX_TRUST_PATH_LEN; pathlen++)
        {
-               issuer = get_issuer_cert(this, current, TRUE);
+               issuer = get_issuer_cert(this, current, TRUE, &scheme);
                if (issuer)
                {
                        /* accept only self-signed CAs as trust anchor */
-                       if (this->cache->issued_by(this->cache, issuer, issuer))
+                       if (issued_by(this, issuer, issuer, NULL))
                        {
                                auth->add(auth, AUTH_RULE_CA_CERT, issuer->get_ref(issuer));
                                DBG1(DBG_CFG, "  using trusted ca certificate \"%Y\"",
@@ -1177,43 +745,51 @@ static bool verify_trust_chain(private_credential_manager_t *this,
                                DBG1(DBG_CFG, "  using trusted intermediate ca certificate "
                                         "\"%Y\"", issuer->get_subject(issuer));
                        }
+                       auth->add(auth, AUTH_RULE_SIGNATURE_SCHEME, scheme);
                }
                else
                {
-                       issuer = get_issuer_cert(this, current, FALSE);
+                       issuer = get_issuer_cert(this, current, FALSE, &scheme);
                        if (issuer)
                        {
                                if (current->equals(current, issuer))
                                {
-                                       DBG1(DBG_CFG, "  self-signed certificate \"%Y\" is not trusted",
-                                                current->get_subject(current));
+                                       DBG1(DBG_CFG, "  self-signed certificate \"%Y\" is not "
+                                                "trusted", current->get_subject(current));
                                        issuer->destroy(issuer);
+                                       call_hook(this, CRED_HOOK_UNTRUSTED_ROOT, current);
                                        break;
                                }
                                auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer));
                                DBG1(DBG_CFG, "  using untrusted intermediate certificate "
                                         "\"%Y\"", issuer->get_subject(issuer));
+                               auth->add(auth, AUTH_RULE_SIGNATURE_SCHEME, scheme);
                        }
                        else
                        {
                                DBG1(DBG_CFG, "no issuer certificate found for \"%Y\"",
                                         current->get_subject(current));
+                               call_hook(this, CRED_HOOK_NO_ISSUER, current);
                                break;
                        }
                }
-               if (!check_certificate(this, current, issuer, online, pathlen,
-                                                          current == subject ? auth : NULL))
+               if (!check_certificate(this, current, issuer, online,
+                                                          pathlen, trusted, auth))
                {
                        trusted = FALSE;
                        issuer->destroy(issuer);
                        break;
                }
+               if (issuer)
+               {
+                       get_key_strength(issuer, auth);
+               }
                current->destroy(current);
                current = issuer;
                if (trusted)
                {
-                       DBG1(DBG_CFG, "  reached self-signed root ca with a path length of %d",
-                                                 pathlen);
+                       DBG1(DBG_CFG, "  reached self-signed root ca with a "
+                                "path length of %d", pathlen);
                        break;
                }
        }
@@ -1221,6 +797,7 @@ static bool verify_trust_chain(private_credential_manager_t *this,
        if (pathlen > MAX_TRUST_PATH_LEN)
        {
                DBG1(DBG_CFG, "maximum path length of %d exceeded", MAX_TRUST_PATH_LEN);
+               call_hook(this, CRED_HOOK_EXCEEDED_PATH_LEN, subject);
        }
        if (trusted)
        {
@@ -1231,6 +808,14 @@ static bool verify_trust_chain(private_credential_manager_t *this,
 }
 
 /**
+ * List find match function for certificates
+ */
+static bool cert_equals(certificate_t *a, certificate_t *b)
+{
+       return a->equals(a, b);
+}
+
+/**
  * enumerator for trusted certificates
  */
 typedef struct {
@@ -1250,6 +835,8 @@ typedef struct {
        certificate_t *pretrusted;
        /** currently enumerating auth config */
        auth_cfg_t *auth;
+       /** list of failed candidates */
+       linked_list_t *failed;
 } trusted_enumerator_t;
 
 METHOD(enumerator_t, trusted_enumerate, bool,
@@ -1272,16 +859,18 @@ METHOD(enumerator_t, trusted_enumerate, bool,
                        /* if we find a trusted self signed certificate, we just accept it.
                         * However, in order to fulfill authorization rules, we try to build
                         * the trust chain if it is not self signed */
-                       if (this->this->cache->issued_by(this->this->cache,
-                                                                  this->pretrusted, this->pretrusted) ||
+                       if (issued_by(this->this, this->pretrusted, this->pretrusted, NULL) ||
                                verify_trust_chain(this->this, this->pretrusted, this->auth,
                                                                   TRUE, this->online))
                        {
-                               this->auth->add(this->auth, AUTH_RULE_SUBJECT_CERT,
-                                                               this->pretrusted->get_ref(this->pretrusted));
                                DBG1(DBG_CFG, "  using trusted certificate \"%Y\"",
                                         this->pretrusted->get_subject(this->pretrusted));
                                *cert = this->pretrusted;
+                               if (!this->auth->get(this->auth, AUTH_RULE_SUBJECT_CERT))
+                               {       /* add cert to auth info, if not returned by trustchain */
+                                       this->auth->add(this->auth, AUTH_RULE_SUBJECT_CERT,
+                                                                       this->pretrusted->get_ref(this->pretrusted));
+                               }
                                if (auth)
                                {
                                        *auth = this->auth;
@@ -1299,6 +888,12 @@ METHOD(enumerator_t, trusted_enumerate, bool,
                        continue;
                }
 
+               if (this->failed->find_first(this->failed, (void*)cert_equals,
+                                                                        NULL, current) == SUCCESS)
+               {       /* check each candidate only once */
+                       continue;
+               }
+
                DBG1(DBG_CFG, "  using certificate \"%Y\"",
                         current->get_subject(current));
                if (verify_trust_chain(this->this, current, this->auth, FALSE,
@@ -1311,6 +906,7 @@ METHOD(enumerator_t, trusted_enumerate, bool,
                        }
                        return TRUE;
                }
+               this->failed->insert_last(this->failed, current->get_ref(current));
        }
        return FALSE;
 }
@@ -1321,14 +917,15 @@ METHOD(enumerator_t, trusted_destroy, void,
        DESTROY_IF(this->pretrusted);
        DESTROY_IF(this->auth);
        DESTROY_IF(this->candidates);
+       this->failed->destroy_offset(this->failed, offsetof(certificate_t, destroy));
+       /* check for delayed certificate cache queue */
+       cache_queue(this->this);
        free(this);
 }
 
-/**
- * create an enumerator over trusted certificates and their trustchain
- */
-static enumerator_t *create_trusted_enumerator(private_credential_manager_t *this,
-                                       key_type_t type, identification_t *id, bool online)
+METHOD(credential_manager_t, create_trusted_enumerator, enumerator_t*,
+       private_credential_manager_t *this, key_type_t type,
+       identification_t *id, bool online)
 {
        trusted_enumerator_t *enumerator;
 
@@ -1341,6 +938,7 @@ static enumerator_t *create_trusted_enumerator(private_credential_manager_t *thi
                .type = type,
                .id = id,
                .online = online,
+               .failed = linked_list_create(),
        );
        return &enumerator->public;
 }
@@ -1390,7 +988,6 @@ METHOD(enumerator_t, public_destroy, void,
                this->wrapper->destroy(this->wrapper);
        }
        this->this->lock->unlock(this->this->lock);
-
        /* check for delayed certificate cache queue */
        cache_queue(this->this);
        free(this);
@@ -1398,7 +995,7 @@ METHOD(enumerator_t, public_destroy, void,
 
 METHOD(credential_manager_t, create_public_enumerator, enumerator_t*,
        private_credential_manager_t *this, key_type_t type, identification_t *id,
-       auth_cfg_t *auth)
+       auth_cfg_t *auth, bool online)
 {
        public_enumerator_t *enumerator;
 
@@ -1407,20 +1004,20 @@ METHOD(credential_manager_t, create_public_enumerator, enumerator_t*,
                        .enumerate = (void*)_public_enumerate,
                        .destroy = _public_destroy,
                },
-               .inner = create_trusted_enumerator(this, type, id, TRUE),
+               .inner = create_trusted_enumerator(this, type, id, online),
                .this = this,
        );
        if (auth)
        {
                enumerator->wrapper = auth_cfg_wrapper_create(auth);
-               add_local_set(this, &enumerator->wrapper->set);
+               add_local_set(this, &enumerator->wrapper->set, FALSE);
        }
        this->lock->read_lock(this->lock);
        return &enumerator->public;
 }
 
 /**
- * Check if a certificate's keyid is contained in the auth helper
+ * Check if a helper contains a certificate as trust anchor
  */
 static bool auth_contains_cacert(auth_cfg_t *auth, certificate_t *cert)
 {
@@ -1452,17 +1049,10 @@ static auth_cfg_t *build_trustchain(private_credential_manager_t *this,
        certificate_t *issuer, *current;
        auth_cfg_t *trustchain;
        int pathlen = 0;
+       bool has_anchor;
 
        trustchain = auth_cfg_create();
-
-       current = auth->get(auth, AUTH_RULE_CA_CERT);
-       if (!current)
-       {
-               /* no trust anchor specified, return this cert only */
-               trustchain->add(trustchain, AUTH_RULE_SUBJECT_CERT,
-                                               subject->get_ref(subject));
-               return trustchain;
-       }
+       has_anchor = auth->get(auth, AUTH_RULE_CA_CERT) != NULL;
        current = subject->get_ref(subject);
        while (TRUE)
        {
@@ -1477,24 +1067,39 @@ static auth_cfg_t *build_trustchain(private_credential_manager_t *this,
                }
                else
                {
+                       if (!has_anchor && issued_by(this, current, current, NULL))
+                       {       /* If no trust anchor specified, accept any CA */
+                               trustchain->add(trustchain, AUTH_RULE_CA_CERT, current);
+                               return trustchain;
+                       }
                        trustchain->add(trustchain, AUTH_RULE_IM_CERT, current);
                }
-               issuer = get_issuer_cert(this, current, FALSE);
-               if (!issuer || issuer->equals(issuer, current) ||
-                       pathlen > MAX_TRUST_PATH_LEN)
+               if (pathlen++ > MAX_TRUST_PATH_LEN)
                {
-                       DESTROY_IF(issuer);
+                       break;
+               }
+               issuer = get_issuer_cert(this, current, FALSE, NULL);
+               if (!issuer)
+               {
+                       if (!has_anchor)
+                       {       /* If no trust anchor specified, accept incomplete chains */
+                               return trustchain;
+                       }
+                       break;
+               }
+               if (has_anchor && issuer->equals(issuer, current))
+               {
+                       issuer->destroy(issuer);
                        break;
                }
                current = issuer;
-               pathlen++;
        }
        trustchain->destroy(trustchain);
        return NULL;
 }
 
 /**
- * find a private key of a give certificate
+ * find a private key of a given certificate
  */
 static private_key_t *get_private_by_cert(private_credential_manager_t *this,
                                                                                  certificate_t *cert, key_type_t type)
@@ -1507,7 +1112,7 @@ static private_key_t *get_private_by_cert(private_credential_manager_t *this,
        public = cert->get_public_key(cert);
        if (public)
        {
-               if (public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
+               if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk))
                {
                        keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
                        private = get_private_by_keyid(this, type, keyid);
@@ -1518,6 +1123,29 @@ static private_key_t *get_private_by_cert(private_credential_manager_t *this,
        return private;
 }
 
+/**
+ * Move the actually used certificate to front, so it gets returned with get()
+ */
+static void prefer_cert(auth_cfg_t *auth, certificate_t *cert)
+{
+       enumerator_t *enumerator;
+       auth_rule_t rule;
+       certificate_t *current;
+
+       enumerator = auth->create_enumerator(auth);
+       while (enumerator->enumerate(enumerator, &rule, &current))
+       {
+               if (rule == AUTH_RULE_SUBJECT_CERT)
+               {
+                       current->get_ref(current);
+                       auth->replace(auth, enumerator, AUTH_RULE_SUBJECT_CERT, cert);
+                       cert = current;
+               }
+       }
+       enumerator->destroy(enumerator);
+       auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert);
+}
+
 METHOD(credential_manager_t, get_private, private_key_t*,
        private_credential_manager_t *this, key_type_t type, identification_t *id,
        auth_cfg_t *auth)
@@ -1526,6 +1154,7 @@ METHOD(credential_manager_t, get_private, private_key_t*,
        certificate_t *cert;
        private_key_t *private = NULL;
        auth_cfg_t *trustchain;
+       auth_rule_t rule;
 
        /* check if this is a lookup by key ID, and do it if so */
        if (id && id->get_type(id) == ID_KEY_ID)
@@ -1537,42 +1166,73 @@ METHOD(credential_manager_t, get_private, private_key_t*,
                }
        }
 
-       /* if a specific certificate is preferred, check for a matching key */
-       cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
-       if (cert)
+       if (auth)
        {
-               private = get_private_by_cert(this, cert, type);
-               if (private)
+               /* try to find a trustchain with one of the configured subject certs */
+               enumerator = auth->create_enumerator(auth);
+               while (enumerator->enumerate(enumerator, &rule, &cert))
                {
-                       trustchain = build_trustchain(this, cert, auth);
-                       if (trustchain)
+                       if (rule == AUTH_RULE_SUBJECT_CERT)
                        {
-                               auth->merge(auth, trustchain, FALSE);
-                               trustchain->destroy(trustchain);
+                               private = get_private_by_cert(this, cert, type);
+                               if (private)
+                               {
+                                       trustchain = build_trustchain(this, cert, auth);
+                                       if (trustchain)
+                                       {
+                                               auth->merge(auth, trustchain, FALSE);
+                                               prefer_cert(auth, cert->get_ref(cert));
+                                               trustchain->destroy(trustchain);
+                                               break;
+                                       }
+                                       private->destroy(private);
+                                       private = NULL;
+                               }
                        }
+               }
+               enumerator->destroy(enumerator);
+               if (private)
+               {
                        return private;
                }
-       }
 
-       /* try to build a trust chain for each certificate found */
-       enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE);
-       while (enumerator->enumerate(enumerator, &cert))
-       {
-               private = get_private_by_cert(this, cert, type);
-               if (private)
+               /* if none yielded a trustchain, enforce the first configured cert */
+               cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
+               if (cert)
+               {
+                       private = get_private_by_cert(this, cert, type);
+                       if (private)
+                       {
+                               trustchain = build_trustchain(this, cert, auth);
+                               if (trustchain)
+                               {
+                                       auth->merge(auth, trustchain, FALSE);
+                                       trustchain->destroy(trustchain);
+                               }
+                               return private;
+                       }
+               }
+
+               /* try to build a trust chain for each certificate found */
+               enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE);
+               while (enumerator->enumerate(enumerator, &cert))
                {
-                       trustchain = build_trustchain(this, cert, auth);
-                       if (trustchain)
+                       private = get_private_by_cert(this, cert, type);
+                       if (private)
                        {
-                               auth->merge(auth, trustchain, FALSE);
-                               trustchain->destroy(trustchain);
-                               break;
+                               trustchain = build_trustchain(this, cert, auth);
+                               if (trustchain)
+                               {
+                                       auth->merge(auth, trustchain, FALSE);
+                                       trustchain->destroy(trustchain);
+                                       break;
+                               }
+                               private->destroy(private);
+                               private = NULL;
                        }
-                       private->destroy(private);
-                       private = NULL;
                }
+               enumerator->destroy(enumerator);
        }
-       enumerator->destroy(enumerator);
 
        /* if no valid trustchain was found, fall back to the first usable cert */
        if (!private)
@@ -1583,7 +1243,10 @@ METHOD(credential_manager_t, get_private, private_key_t*,
                        private = get_private_by_cert(this, cert, type);
                        if (private)
                        {
-                               auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert->get_ref(cert));
+                               if (auth)
+                               {
+                                       auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert->get_ref(cert));
+                               }
                                break;
                        }
                }
@@ -1595,7 +1258,10 @@ METHOD(credential_manager_t, get_private, private_key_t*,
 METHOD(credential_manager_t, flush_cache, void,
        private_credential_manager_t *this, certificate_type_t type)
 {
-       this->cache->flush(this->cache, type);
+       if (this->cache)
+       {
+               this->cache->flush(this->cache, type);
+       }
 }
 
 METHOD(credential_manager_t, add_set, void,
@@ -1618,7 +1284,7 @@ METHOD(credential_manager_t, add_validator, void,
        private_credential_manager_t *this, cert_validator_t *vdtr)
 {
        this->lock->write_lock(this->lock);
-       this->sets->insert_last(this->validators, vdtr);
+       this->validators->insert_last(this->validators, vdtr);
        this->lock->unlock(this->lock);
 }
 
@@ -1635,10 +1301,14 @@ METHOD(credential_manager_t, destroy, void,
 {
        cache_queue(this);
        this->cache_queue->destroy(this->cache_queue);
-       this->sets->remove(this->sets, this->cache, NULL);
+       if (this->cache)
+       {
+               this->sets->remove(this->sets, this->cache, NULL);
+               this->cache->destroy(this->cache);
+       }
        this->sets->destroy(this->sets);
        this->local_sets->destroy(this->local_sets);
-       this->cache->destroy(this->cache);
+       this->exclusive_local_sets->destroy(this->exclusive_local_sets);
        this->validators->destroy(this->validators);
        this->lock->destroy(this->lock);
        this->queue_mutex->destroy(this->queue_mutex);
@@ -1660,25 +1330,35 @@ credential_manager_t *credential_manager_create()
                        .get_cert = _get_cert,
                        .get_shared = _get_shared,
                        .get_private = _get_private,
+                       .create_trusted_enumerator = _create_trusted_enumerator,
                        .create_public_enumerator = _create_public_enumerator,
                        .flush_cache = _flush_cache,
                        .cache_cert = _cache_cert,
+                       .issued_by = _issued_by,
                        .add_set = _add_set,
                        .remove_set = _remove_set,
+                       .add_local_set = _add_local_set,
+                       .remove_local_set = _remove_local_set,
                        .add_validator = _add_validator,
                        .remove_validator = _remove_validator,
+                       .set_hook = _set_hook,
+                       .call_hook = _call_hook,
                        .destroy = _destroy,
                },
                .sets = linked_list_create(),
                .validators = linked_list_create(),
-               .cache = cert_cache_create(),
                .cache_queue = linked_list_create(),
                .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
                .queue_mutex = mutex_create(MUTEX_TYPE_DEFAULT),
        );
 
        this->local_sets = thread_value_create((thread_cleanup_t)this->sets->destroy);
-       this->sets->insert_first(this->sets, this->cache);
+       this->exclusive_local_sets = thread_value_create((thread_cleanup_t)this->sets->destroy);
+       if (lib->settings->get_bool(lib->settings, "%s.cert_cache", TRUE, lib->ns))
+       {
+               this->cache = cert_cache_create();
+               this->sets->insert_first(this->sets, this->cache);
+       }
 
        return &this->public;
 }