fixed crash if crl fetching fails
[strongswan.git] / src / charon / credentials / credential_manager.c
index 7b16c52..ac97f73 100644 (file)
@@ -20,6 +20,9 @@
 #include <daemon.h>
 #include <utils/linked_list.h>
 #include <utils/mutex.h>
+#include <credentials/sets/cert_cache.h>
+#include <credentials/sets/auth_info_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>
@@ -45,6 +48,11 @@ struct private_credential_manager_t {
        linked_list_t *sets;
        
        /**
+        * trust relationship and certificate cache
+        */
+       cert_cache_t *cache;
+       
+       /**
         * mutex to gain exclusive access
         */
        mutex_t *mutex;
@@ -295,115 +303,9 @@ static shared_key_t *get_shared(private_credential_manager_t *this,
 /**
  * forward declaration 
  */
-static certificate_t *get_trusted_cert(private_credential_manager_t *this,
-                                                                          key_type_t type, identification_t *id,
-                                                                          auth_info_t *auth, bool crl, bool ocsp);
-
-/**
- * credential_set_t implementation around an OCSP response
- */
-typedef struct ocsp_wrapper_t {
-       credential_set_t set;
-       ocsp_response_t *response;
-} ocsp_wrapper_t;
-
-/**
- * enumerator for ocsp_wrapper_t.create_cert_enumerator()
- */
-typedef struct {
-       enumerator_t public;
-       enumerator_t *inner;
-       certificate_type_t cert;
-       key_type_t key;
-       identification_t *id;
-} ocsp_wrapper_enumerator_t;
-
-/**
- * enumerate function for ocsp_wrapper_enumerator_t
- */
-static bool ocsp_wrapper_enum_enumerate(ocsp_wrapper_enumerator_t *this,
-                                                                           certificate_t **cert)
-{
-       certificate_t *current;
-       public_key_t *public;
-
-       while (this->inner->enumerate(this->inner, &current))
-       {
-               if (this->cert != CERT_ANY && this->cert != current->get_type(current))
-               {       /* CERT type requested, but does not match */
-                       continue;
-               }
-               public = current->get_public_key(current);
-               if (this->key != KEY_ANY && !public)
-               {       /* key type requested, but no public key */
-                       DESTROY_IF(public);
-                       continue;
-               }
-               if (this->key != KEY_ANY && public && this->key != public->get_type(public))
-               {       /* key type requested, but public key has another type */
-                       DESTROY_IF(public);
-                       continue;
-               }
-               DESTROY_IF(public);
-               if (this->id && !current->has_subject(current, this->id))
-               {       /* subject requested, but does not match */
-                       continue;
-               }
-               *cert = current;
-               return TRUE;
-       }
-       return FALSE;
-}
-
-/**
- * destroy function for ocsp_wrapper_enumerator_t
- */
-static void ocsp_wrapper_enum_destroy(ocsp_wrapper_enumerator_t *this)
-{
-       this->inner->destroy(this->inner);
-       free(this);
-}
-
-/**
- * implementation of ocsp_wrapper_t.set.create_cert_enumerator
- */
-static enumerator_t *ocsp_wrapper_create_enumerator(ocsp_wrapper_t *this,
-                                                                               certificate_type_t cert, key_type_t key,
-                                                                               identification_t *id, bool trusted)
-{
-       ocsp_wrapper_enumerator_t *enumerator;
-       
-       if (trusted)
-       {
-               return NULL;
-       }
-       
-       enumerator = malloc_thing(ocsp_wrapper_enumerator_t);
-       enumerator->cert = cert;
-       enumerator->key = key;
-       enumerator->id = id;
-       enumerator->inner = this->response->create_cert_enumerator(this->response);
-       enumerator->public.enumerate = (void*)ocsp_wrapper_enum_enumerate;
-       enumerator->public.destroy = (void*)ocsp_wrapper_enum_destroy;
-       return &enumerator->public;
-}
-
-/**
- * create credential_set wrapper around an OCSP response
- */
-static ocsp_wrapper_t *ocsp_wrapper_create(ocsp_response_t *response)
-{
-       ocsp_wrapper_t *this = malloc_thing(ocsp_wrapper_t);
-       
-       this->response = response;
-       this->set.create_private_enumerator = (void*)return_null;
-       this->set.create_cert_enumerator = (void*)ocsp_wrapper_create_enumerator;
-       this->set.create_shared_enumerator = (void*)return_null;
-       this->set.create_cdp_enumerator = (void*)return_null;
-
-       return this;
-}
 
+static enumerator_t *create_trusted_enumerator(private_credential_manager_t *this,
+                                       key_type_t type, identification_t *id, bool crl, bool ocsp);
 /**
  * Do an OCSP request
  */
@@ -420,20 +322,21 @@ static certificate_t *fetch_ocsp(private_credential_manager_t *this, char *url,
                                                BUILD_CERT, subject->get_ref(subject), BUILD_END);
        if (!request)
        {
-               DBG1(DBG_CFG, "  generating ocsp request failed");
+               DBG1(DBG_CFG, "generating ocsp request failed");
                return NULL;
        }
        
        send = request->get_encoding(request);
        request->destroy(request);
 
-       DBG1(DBG_CFG, "requesting ocsp response from '%s' ...", url);
+       DBG1(DBG_CFG, "requesting ocsp status from '%s' ...", url);
+       /* TODO: unlock manager while fetching? */
        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);
+               DBG1(DBG_CFG, "ocsp request to %s failed", url);
                chunk_free(&send);
                return NULL;
        }
@@ -444,46 +347,111 @@ static certificate_t *fetch_ocsp(private_credential_manager_t *this, char *url,
                                                                  BUILD_BLOB_ASN1_DER, receive, BUILD_END);
        if (!response)
        {
-               DBG1(DBG_CFG, "  parsing ocsp response failed");
+               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);
+       this->sets->remove(this->sets, this->cache, NULL);
+       this->sets->insert_first(this->sets, wrapper);
+       this->sets->insert_first(this->sets, this->cache);
        
+       subject = &response->certificate;
+       responder = subject->get_issuer(subject);
+       enumerator = create_trusted_enumerator(this, KEY_ANY, responder, FALSE, FALSE);
+       while (enumerator->enumerate(enumerator, &issuer, NULL))
+       {
+               if (this->cache->issued_by(this->cache, subject, issuer))
+               {
+                       verified = TRUE;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       
+       this->sets->remove(this->sets, wrapper, NULL);
+       wrapper->destroy(wrapper);
+       return verified;
+}
 
-       /* verify the signature of the ocsp response */
+/**
+ * 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)
+{
+       ocsp_response_t *response;
+       time_t revocation, this_update, next_update, valid_until;
+       crl_reason_t reason;
+       
+       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, crl_reason_names, reason);
+                       DESTROY_IF(best);
+                       *valid = VALIDATION_REVOKED;
+                       return cand;
+               case VALIDATION_GOOD:
+                       /* results in either good or stale */
+                       break;
+               default:
+               case VALIDATION_FAILED:
+                       /* candidate unusable, does not contain our cert */
+                       cand->destroy(cand);
+                       return best;
+       }
+
+       /* select the better of the two responses */
+       if (best == NULL || cand->is_newer(cand, best))
        {
-               certificate_t *issuer_cert;
-               identification_t *responder;
-               auth_info_t *auth;
-               ocsp_wrapper_t *wrapper;
-               bool ok;
-               auth = auth_info_create();
-               wrapper = ocsp_wrapper_create((ocsp_response_t*)response);
-               this->sets->insert_first(this->sets, wrapper);
-               responder = response->get_issuer(response);
-               issuer_cert = get_trusted_cert(this, KEY_ANY, responder, auth, FALSE, FALSE);
-               this->sets->remove(this->sets, wrapper, NULL);
-               free(wrapper);
-               auth->destroy(auth);
-
-               if (!issuer_cert)
+               DESTROY_IF(best);
+               best = cand;
+               if (best->get_validity(best, NULL, NULL, &valid_until))
                {
-                       DBG1(DBG_CFG, "  ocsp response untrusted: no signer certificate found");
-                       response->destroy(response);
-                       return NULL;
+                       DBG1(DBG_CFG, "  ocsp response is valid: until %#T", &valid_until);
+                       *valid = VALIDATION_GOOD;
                }
-               ok = response->issued_by(response, issuer_cert, TRUE);
-               issuer_cert->destroy(issuer_cert);
-               DBG1(DBG_CFG, "  ocsp response is %strusted: %s signature",
-                                                ok ? "":"un", ok ? "good" : "bad");
-               if (!ok)
+               else
                {
-                       response->destroy(response);
-                       return NULL;
+                       DBG1(DBG_CFG, "  ocsp response is stale: since %#T", &valid_until);
+                       *valid = VALIDATION_STALE;
                }
        }
-       /* TODO: cache response? */
-       return response;
+       else
+       {
+               *valid = VALIDATION_STALE;
+               cand->destroy(cand);
+       }
+       return best;
 }
 
 /**
@@ -493,158 +461,85 @@ static cert_validation_t check_ocsp(private_credential_manager_t *this,
                                                                    x509_t *subject, x509_t *issuer, 
                                                                    auth_info_t *auth)
 {
-       certificate_t *sub = (certificate_t*)subject;
-       certificate_t *best_cert = NULL;
-       certificate_t *cert;
-       public_key_t *public;
+       enumerator_t *enumerator;
        cert_validation_t valid = VALIDATION_SKIPPED;
+       certificate_t *best = NULL, *current;
        identification_t *keyid = NULL;
-       bool stale = TRUE;
-       
-       /* derive the authorityKeyIdentifier from the issuer's public key */
-       cert = &issuer->interface;
-       public = cert->get_public_key(cert);
-       if (public)
-       {
-               keyid = public->get_id(public, ID_PUBKEY_SHA1);
-       }
-       
-       /* find a cached ocsp response by authorityKeyIdentifier */     
-       if (keyid)
-       {
-               enumerator_t *enumerator = create_cert_enumerator(this,
-                                                                                CERT_X509_OCSP_RESPONSE,
-                                                                                KEY_ANY, keyid, TRUE);
-               certificate_t *cert;
+       public_key_t *public;
+       char *uri = NULL;
 
-               while (enumerator->enumerate(enumerator, &cert))
+       /** 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);
+               if (best && valid != VALIDATION_STALE)
                {
-                       if (cert->has_subject(cert, sub->get_subject(sub)))
-                       {
-                               /* select most recent ocsp response */
-                               if (best_cert == NULL || cert->is_newer(cert, best_cert))
-                               {
-                                       DESTROY_IF(best_cert);
-                                       best_cert = cert->get_ref(cert);
-                               }
-                       }
+                       DBG1(DBG_CFG, "found cached ocsp response");
+                       break;
                }
-               enumerator->destroy(enumerator);
        }
-
-       /* check the validity of the cached ocsp response if one was found */
-       if (best_cert)
+       enumerator->destroy(enumerator);
+       
+       /* derive the authorityKeyIdentifier from the issuer's public key */
+       current = &issuer->interface;
+       public = current->get_public_key(current);
+       if (public)
        {
-               stale = !best_cert->get_validity(best_cert, NULL, NULL, NULL);
-               DBG1(DBG_CFG, "cached ocsp response is %s", stale? "stale":"valid");
+               keyid = public->get_id(public, ID_PUBKEY_SHA1);
        }
-
-       /* fallback to URL fetching from CDPs */
-       if (stale && keyid)
+       /** fetch from configured OCSP responder URLs */
+       if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
        {
-               enumerator_t *enumerator = create_cdp_enumerator(this,
-                                                                                CERT_X509_OCSP_RESPONSE, keyid);
-               char *uri;
-
+               enumerator = create_cdp_enumerator(this, CERT_X509_OCSP_RESPONSE, keyid);
                while (enumerator->enumerate(enumerator, &uri))
                {
-                       certificate_t* cert = fetch_ocsp(this, uri, &subject->interface,
-                                                                                                               &issuer->interface);
-
-                       /* redefine default since we have at least one uri */
-                       valid = VALIDATION_FAILED;
-
-                       if (cert)
+                       current = fetch_ocsp(this, uri, &subject->interface,
+                                                                &issuer->interface);
+                       if (current)
                        {
-                               /* select most recent ocsp response until valid one is found */
-                               if (best_cert == NULL || cert->is_newer(cert, best_cert))
+                               best = get_better_ocsp(this, current, best, subject, issuer, &valid);
+                               if (best && valid != VALIDATION_STALE)
                                {
-                                       DESTROY_IF(best_cert);
-                                       best_cert = cert;
-                                       stale = !best_cert->get_validity(best_cert, NULL, NULL, NULL);
-                                       DBG1(DBG_CFG, "ocsp response is %s", stale? "stale":"valid");
-                                       if (!stale)
-                                       {
-                                               break;
-                                       }
-                               }
-                               else
-                               {
-                                       cert->destroy(cert);
+                                       break;
                                }
                        }
                }
                enumerator->destroy(enumerator);
        }
+       DESTROY_IF(public);
 
        /* fallback to URL fetching from subject certificate's URIs */
-       if (stale)
+       if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
        {
-               enumerator_t *enumerator = subject->create_ocsp_uri_enumerator(subject);
-               char *uri;
-
+               enumerator = subject->create_ocsp_uri_enumerator(subject);
                while (enumerator->enumerate(enumerator, &uri))
                {
-                       certificate_t* cert = fetch_ocsp(this, uri, &subject->interface,
-                                                                                                               &issuer->interface);
-
-                       /* redefine default since we have at least one uri */
-                       valid = VALIDATION_FAILED;
-
-                       if (cert)
+                       current = fetch_ocsp(this, uri, &subject->interface,
+                                                                &issuer->interface);
+                       if (current)
                        {
-                               /* select most recent ocsp response until valid one is found */
-                               if (best_cert == NULL || cert->is_newer(cert, best_cert))
-                               {
-                                       DESTROY_IF(best_cert);
-                                       best_cert = cert;
-                                       stale = !best_cert->get_validity(best_cert, NULL, NULL, NULL);
-                                       DBG1(DBG_CFG, "ocsp response is %s", stale? "stale":"valid");
-                                       if (!stale)
-                                       {
-                                               break;
-                                       }
-                               }
-                               else
+                               best = get_better_ocsp(this, current, best, subject, issuer, &valid);
+                               if (best && valid != VALIDATION_STALE)
                                {
-                                       cert->destroy(cert);
+                                       break;
                                }
                        }
                }
                enumerator->destroy(enumerator);
        }
-       DESTROY_IF(public);
-
-       /* if we have an ocsp response, check the revocation status */
-       if (best_cert)
+       /* an uri was found, but no result. switch validation state to failed */
+       if (valid == VALIDATION_SKIPPED && uri)
        {
-               time_t revocation, this_update, next_update;
-               crl_reason_t reason;
-               ocsp_response_t *response = (ocsp_response_t*)best_cert;
-               
-               valid = response->get_status(response, subject, issuer, &revocation,
-                                                                        &reason, &this_update, &next_update);
-               switch (valid)
-               {
-                       case VALIDATION_FAILED:
-                               DBG1(DBG_CFG, "subject not found in ocsp response");
-                               break;
-                       case VALIDATION_REVOKED:
-                               DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
-                                                         &revocation, crl_reason_names, reason);
-                               break;
-                       case VALIDATION_GOOD:
-                       case VALIDATION_UNKNOWN:
-                       default:
-                               break;
-               }
-               best_cert->destroy(best_cert);
+               valid = VALIDATION_FAILED;
        }
-       
        if (auth)
        {
                auth->add_item(auth, AUTHZ_OCSP_VALIDATION, &valid);
        }
+       DESTROY_IF(best);
        return valid;
 }
 
@@ -653,51 +548,112 @@ static cert_validation_t check_ocsp(private_credential_manager_t *this,
  */
 static certificate_t* fetch_crl(private_credential_manager_t *this, char *url)
 {
-       certificate_t *crl_cert;
+       certificate_t *crl;
        chunk_t chunk;
        
        /* TODO: unlock the manager while fetching? */
        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");
+               DBG1(DBG_CFG, "crl fetching failed");
                return NULL;
        }
-       crl_cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
-                                                                 BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
-       if (!crl_cert)
+       crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
+                                                        BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
+       if (!crl)
        {
-               DBG1(DBG_CFG, "  crl fetched successfully but parsing failed");
+               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, FALSE);
+       while (enumerator->enumerate(enumerator, &issuer, NULL))
+       {
+               if (this->cache->issued_by(this->cache, crl, issuer))
+               {
+                       DBG1(DBG_CFG, "  crl correctly signed by \"%D\"",
+                                                  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)
+{
+       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;
+       }
        
-       /* verify the signature of the fetched crl */
+       crl = (crl_t*)cand;
+       enumerator = crl->create_enumerator(crl);
+       while (enumerator->enumerate(enumerator, &serial, &revocation, &reason))
        {
-               bool ok;
-               identification_t *issuer = crl_cert->get_issuer(crl_cert);
-               auth_info_t *auth = auth_info_create();
-               certificate_t *issuer_cert = get_trusted_cert(this, KEY_ANY, issuer,
-                                                                                                         auth, FALSE, FALSE);
-               auth->destroy(auth);
-
-               if (!issuer_cert)
+               if (chunk_equals(serial, subject->get_serial(subject)))
                {
-                       DBG1(DBG_CFG, "  crl is untrusted: issuer certificate not found");
-                       crl_cert->destroy(crl_cert);
-                       return NULL;
+                       DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
+                                &revocation, crl_reason_names, reason);
+                       *valid = VALIDATION_REVOKED;
+                       enumerator->destroy(enumerator);
+                       DESTROY_IF(best);
+                       return cand;
                }
-               ok = crl_cert->issued_by(crl_cert, issuer_cert, TRUE);
-               issuer_cert->destroy(issuer_cert);
+       }
+       enumerator->destroy(enumerator);
 
-               DBG1(DBG_CFG, "  crl is %strusted: %s signature",
-                                          ok ? "":"un", ok ? "good" : "bad");
-               if (!ok)
+       /* select the better of the two CRLs */
+       if (best == NULL || cand->is_newer(cand, 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);
+                       *valid = VALIDATION_GOOD;
+               }
+               else
                {
-                       crl_cert->destroy(crl_cert);
-                       return NULL;
+                       DBG1(DBG_CFG, "  crl is stale: since %#T", &valid_until);
+                       *valid = VALIDATION_STALE;
                }
        }
-       return crl_cert;
+       else
+       {
+               *valid = VALIDATION_STALE;
+               cand->destroy(cand);
+       }
+       return best;
 }
 
 /**
@@ -707,16 +663,17 @@ static cert_validation_t check_crl(private_credential_manager_t *this,
                                                                   x509_t *subject, x509_t *issuer, 
                                                                   auth_info_t *auth)
 {
+       cert_validation_t valid = VALIDATION_SKIPPED;
        identification_t *keyid = NULL;
-       certificate_t *best_cert = NULL;
-       certificate_t *cert;
+       certificate_t *best = NULL;
+       certificate_t *current;
        public_key_t *public;
-       cert_validation_t valid = VALIDATION_SKIPPED;
-       bool stale = TRUE;
+       enumerator_t *enumerator;
+       char *uri;
        
        /* derive the authorityKeyIdentifier from the issuer's public key */
-       cert = &issuer->interface;
-       public = cert->get_public_key(cert);
+       current = &issuer->interface;
+       public = current->get_public_key(current);
        if (public)
        {
                keyid = public->get_id(public, ID_PUBKEY_SHA1);
@@ -725,134 +682,72 @@ static cert_validation_t check_crl(private_credential_manager_t *this,
        /* find a cached crl by authorityKeyIdentifier */
        if (keyid)
        {
-               enumerator_t *enumerator = create_cert_enumerator(this, CERT_X509_CRL,
-                                                                                                               KEY_ANY, keyid, TRUE);
-               certificate_t *cert;
-
-               while (enumerator->enumerate(enumerator, &cert))
+               enumerator = create_cert_enumerator(this, CERT_X509_CRL, KEY_ANY, 
+                                                                                       keyid, FALSE);
+               while (enumerator->enumerate(enumerator, &current))
                {
-                       /* select most recent crl */
-                       if (best_cert == NULL || cert->is_newer(cert, best_cert))
+                       current->get_ref(current);
+                       best = get_better_crl(this, current, best, subject, issuer, &valid);
+                       if (best && valid != VALIDATION_STALE)
                        {
-                               DESTROY_IF(best_cert);
-                               best_cert = cert->get_ref(cert);
+                               DBG1(DBG_CFG, "found cached crl");
+                               break;
                        }
                }
                enumerator->destroy(enumerator);
        }
 
-       /* check the validity of the cached crl if one was found */
-       if (best_cert)
+       /* fallback to fetching crls from credential sets cdps */
+       if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
        {
-               stale = !best_cert->get_validity(best_cert, NULL, NULL, NULL);
-               DBG1(DBG_CFG, "cached crl is %s", stale? "stale":"valid");
-       }
-
-       /* fallback to fetching crls from cdps defined in ca info sections */
-       if (stale && keyid)
-       {
-               enumerator_t *enumerator = create_cdp_enumerator(this, CERT_X509_CRL,
-                                                                                                                keyid);
-               char *uri;
+               enumerator = create_cdp_enumerator(this, CERT_X509_CRL, keyid);
 
                while (enumerator->enumerate(enumerator, &uri))
                {
-                       certificate_t *cert = fetch_crl(this, uri);
-
-                       /* redefine default since we have at least one uri */
-                       valid = VALIDATION_FAILED;
-
-                       if (cert)
+                       current = fetch_crl(this, uri);
+                       if (current)
                        {
-                               /* select most recent crl until valid one is found */
-                               if (best_cert == NULL || cert->is_newer(cert, best_cert))
+                               best = get_better_crl(this, current, best, subject, issuer, &valid);
+                               if (best && valid != VALIDATION_STALE)
                                {
-                                       DESTROY_IF(best_cert);
-                                       best_cert = cert;
-                                       stale = !best_cert->get_validity(best_cert, NULL, NULL, NULL);
-                                       DBG1(DBG_CFG, "fetched crl is %s", stale? "stale":"valid");
-                                       if (!stale)
-                                       {
-                                               break;
-                                       }
-                               }
-                               else
-                               {
-                                       cert->destroy(cert);
+                                       break;
                                }
                        }
                }
                enumerator->destroy(enumerator);
        }
+       DESTROY_IF(public);
 
-       /* fallback to fetching crls from cdps defined in the subject's certificate */
-       if (stale)
+       /* fallback to fetching crls from cdps from subject's certificate */
+       if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
        {
-               enumerator_t *enumerator = subject->create_crl_uri_enumerator(subject);
-               char *uri;
+               enumerator = subject->create_crl_uri_enumerator(subject);
 
                while (enumerator->enumerate(enumerator, &uri))
                {
-                       certificate_t *cert = fetch_crl(this, uri);
-
-                       /* redefine default since we have at least one uri */
-                       valid = VALIDATION_FAILED;
-
-                       if (cert)
+                       current = fetch_crl(this, uri);
+                       if (current)
                        {
-                               /* select most recent crl until valid one is found */
-                               if (best_cert == NULL || cert->is_newer(cert, best_cert))
+                               best = get_better_crl(this, current, best, subject, issuer, &valid);
+                               if (best && valid != VALIDATION_STALE)
                                {
-                                       DESTROY_IF(best_cert);
-                                       best_cert = cert;
-                                       stale = !best_cert->get_validity(best_cert, NULL, NULL, NULL);
-                                       DBG1(DBG_CFG, "fetched crl is %s", stale? "stale":"valid");
-                                       if (!stale)
-                                       {
-                                               break;
-                                       }
-                               }
-                               else
-                               {
-                                       cert->destroy(cert);
+                                       break;
                                }
                        }
                }
                enumerator->destroy(enumerator);
        }
-       DESTROY_IF(public);
-
-       /* if we have a crl, check the revocation status */
-       if (best_cert)
+       
+       /* an uri was found, but no result. switch validation state to failed */
+       if (valid == VALIDATION_SKIPPED && uri)
        {
-               chunk_t subject_serial = subject->get_serial(subject);
-               chunk_t serial;
-               time_t revocation;
-               crl_reason_t reason;
-               crl_t *crl = (crl_t*)best_cert;
-               enumerator_t *enumerator = crl->create_enumerator(crl);
-
-               /* redefine default */
-               valid = stale ? VALIDATION_UNKNOWN : VALIDATION_GOOD;
-
-               while (enumerator->enumerate(enumerator, &serial, &revocation, &reason))
-               {
-                       if (chunk_equals(serial, subject_serial))
-                       {
-                               DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
-                                        &revocation, crl_reason_names, reason);
-                               valid = VALIDATION_REVOKED;
-                               break;
-                       }
-               }
-               enumerator->destroy(enumerator);
-               best_cert->destroy(best_cert);
+               valid = VALIDATION_FAILED;
        }
-
        if (auth)
        {
                auth->add_item(auth, AUTHZ_CRL_VALIDATION, &valid);
        }
+       DESTROY_IF(best);
        return valid;
 }
 
@@ -864,20 +759,20 @@ static bool check_certificate(private_credential_manager_t *this,
                                                          bool crl, bool ocsp, auth_info_t *auth)
 {
        time_t not_before, not_after;
-
+       
        if (!subject->get_validity(subject, NULL, &not_before, &not_after))
        {
-               DBG1(DBG_CFG, "certificate invalid (valid from %T to %T)",
+               DBG1(DBG_CFG, "subject certificate invalid (valid from %T to %T)",
                         &not_before, &not_after);
                return FALSE;
        }
-       if (issuer && !subject->issued_by(subject, issuer, TRUE))
+       if (!issuer->get_validity(issuer, NULL, &not_before, &not_after))
        {
-               DBG1(DBG_CFG, "certificate not issued by \"%D\"",
-                        subject->get_subject(subject), issuer->get_subject(issuer));
+               DBG1(DBG_CFG, "issuer certificate invalid (valid from %T to %T)",
+                        &not_before, &not_after);
                return FALSE;
        }
-       if (issuer && issuer->get_type(issuer) == CERT_X509 &&
+       if (issuer->get_type(issuer) == CERT_X509 &&
                subject->get_type(subject) == CERT_X509)
        {
                if (ocsp)
@@ -891,11 +786,13 @@ static bool check_certificate(private_credential_manager_t *this,
                                        /* has already been logged */                   
                                        return FALSE;
                                case VALIDATION_SKIPPED:
-                                       DBG2(DBG_CFG, "OCSP check skipped, no OCSP URI found");
+                                       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:
-                               case VALIDATION_UNKNOWN:
-                                       DBG1(DBG_CFG, "OCSP check failed, fallback to CRL");
+                                       DBG1(DBG_CFG, "ocsp check failed, fallback to crl");
                                        break;
                        }
                }
@@ -909,14 +806,12 @@ static bool check_certificate(private_credential_manager_t *this,
                                case VALIDATION_REVOKED:                
                                        /* has already been logged */                   
                                        return FALSE;
-                               case VALIDATION_UNKNOWN:
-                                       DBG1(DBG_CFG, "certificate status is unknown");
-                                       break;
                                case VALIDATION_FAILED:
                                case VALIDATION_SKIPPED:
                                        DBG1(DBG_CFG, "certificate status is not available");
-                                       break;          
-                               default:
+                                       break;
+                               case VALIDATION_STALE:
+                                       DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
                                        break;
                        }
                }
@@ -925,293 +820,324 @@ static bool check_certificate(private_credential_manager_t *this,
 }
 
 /**
- * credential_set_t implementation around a auth_info_t
+ * Get a trusted certificate from a credential set
  */
-typedef struct auth_wrapper_t {
-       credential_set_t set;
+static certificate_t *get_pretrusted_cert(private_credential_manager_t *this,
+                                                                                 key_type_t type, identification_t *id)
+{
+       certificate_t *subject;
+       public_key_t *public;
+       
+       subject = get_cert(this, CERT_ANY, type, id, TRUE);
+       if (!subject)
+       {
+               return NULL;
+       }
+       public = subject->get_public_key(subject);
+       if (!public)
+       {
+               subject->destroy(subject);
+               return NULL;
+       }
+       public->destroy(public);
+       return subject;
+}
+
+/**
+ * Get the issuing certificate of a subject certificate
+ */
+static certificate_t *get_issuer_cert(private_credential_manager_t *this,
+                                                                         certificate_t *subject, bool trusted)
+{
+       enumerator_t *enumerator;
+       certificate_t *issuer = NULL, *candidate;
+       
+       enumerator = create_cert_enumerator(this, subject->get_type(subject), KEY_ANY, 
+                                                                               subject->get_issuer(subject), trusted);
+       while (enumerator->enumerate(enumerator, &candidate))
+       {
+               if (this->cache->issued_by(this->cache, subject, candidate))
+               {
+                       issuer = candidate->get_ref(candidate);
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return issuer;
+}
+
+/**
+ * try to verify the trust chain of subject, return TRUE if trusted
+ */
+static bool verify_trust_chain(private_credential_manager_t *this,
+                                                          certificate_t *subject, auth_info_t *result,
+                                                          bool trusted, bool crl, bool ocsp)
+{
+       certificate_t *current, *issuer;
        auth_info_t *auth;
-} auth_wrapper_t;
+       u_int level = 0;
+       
+       auth = auth_info_create();
+       current = subject->get_ref(subject);
+       while (level++ < MAX_CA_LEVELS)
+       {
+               issuer = get_issuer_cert(this, current, TRUE);
+               if (issuer)
+               {
+                       /* accept only self-signed CAs as trust anchor */
+                       if (this->cache->issued_by(this->cache, issuer, issuer))
+                       {
+                               auth->add_item(auth, AUTHZ_CA_CERT, issuer);
+                               DBG1(DBG_CFG, "  using trusted ca certificate \"%D\"",
+                                        issuer->get_subject(issuer));
+                               trusted = TRUE;
+                       }
+                       else
+                       {
+                               auth->add_item(auth, AUTHZ_IM_CERT, issuer);
+                               DBG1(DBG_CFG, "  using trusted intermediate ca certificate "
+                                        "\"%D\"", issuer->get_subject(issuer));
+                       }
+               }
+               else
+               {
+                       issuer = get_issuer_cert(this, current, FALSE);
+                       if (issuer)
+                       {
+                               if (current->equals(current, issuer))
+                               {
+                                       DBG1(DBG_CFG, "  self-signed certificate \"%D\" is not trusted",
+                                                current->get_subject(current));
+                                       issuer->destroy(issuer);
+                                       break;
+                               }
+                               auth->add_item(auth, AUTHZ_IM_CERT, issuer);
+                               DBG1(DBG_CFG, "  using untrusted intermediate certificate "
+                                        "\"%D\"", issuer->get_subject(issuer));
+                       }
+                       else
+                       {
+                               DBG1(DBG_CFG, "no issuer certificate found for \"%D\"", 
+                                        issuer->get_subject(issuer));
+                               current->destroy(current);
+                               break;
+                       }
+               }
+               if (!check_certificate(this, current, issuer, crl, ocsp,
+                                                          current == subject ? auth : NULL))
+               {
+                       trusted = FALSE;
+                       issuer->destroy(issuer);
+                       break;
+               }
+               current->destroy(current);
+               current = issuer;
+               if (trusted)
+               {
+                       break;
+               }
+       }
+       current->destroy(current);
+       if (trusted)
+       {
+               result->merge(result, auth);
+       }
+       auth->destroy(auth);
+       return trusted;
+}
 
 /**
- * enumerator for auth_wrapper_t.create_cert_enumerator()
+ * enumerator for trusted certificates
  */
 typedef struct {
+       /** implements enumerator_t interface */
        enumerator_t public;
-       enumerator_t *inner;
-       certificate_type_t cert;
-       key_type_t key;
+       /** enumerator over candidate peer certificates */
+       enumerator_t *candidates;
+       /** reference to the credential_manager */
+       private_credential_manager_t *this;
+       /** type of the requested key */
+       key_type_t type;
+       /** identity the requested key belongs to */
        identification_t *id;
-} auth_wrapper_enumerator_t;
+       /** TRUE to do CRL checking */
+       bool crl;
+       /** TRUE to do OCSP checking */
+       bool ocsp;
+       /** currently enumerating certificate */
+       certificate_t *current;
+       /** currently enumerating auth info */
+       auth_info_t *auth;
+} trusted_enumerator_t;
 
 /**
- * enumerate function for auth_wrapper_enumerator_t
+ * Implements trusted_enumerator_t.enumerate
  */
-static bool auth_wrapper_enum_enumerate(auth_wrapper_enumerator_t *this,
-                                                                           certificate_t **cert)
+static bool trusted_enumerate(trusted_enumerator_t *this,
+                                                         certificate_t **cert, auth_info_t **auth)
 {
-       auth_item_t type;
-       certificate_t *current;
-       public_key_t *public;
-
-       while (this->inner->enumerate(this->inner, &type, &current))
+       DESTROY_IF(this->current);
+       DESTROY_IF(this->auth);
+       this->auth = auth_info_create();
+       
+       if (!this->candidates)
        {
-               if (type != AUTHN_SUBJECT_CERT && 
-                       type != AUTHN_IM_CERT)
+               /* first invocation, build enumerator for next one */
+               this->candidates = create_cert_enumerator(this->this, CERT_ANY,
+                                                                                                 this->type, this->id, FALSE);
+               /* check if we have a trusted certificate for that peer */
+               this->current = get_pretrusted_cert(this->this, this->type, this->id);
+               if (this->current)
                {
-                       continue;
-               }
-
-               if (this->cert != CERT_ANY && this->cert != current->get_type(current))
-               {       /* CERT type requested, but does not match */
-                       continue;
-               }
-               public = current->get_public_key(current);
-               if (this->key != KEY_ANY && !public)
-               {       /* key type requested, but no public key */
-                       DESTROY_IF(public);
-                       continue;
-               }
-               if (this->key != KEY_ANY && public && this->key != public->get_type(public))
-               {       /* key type requested, but public key has another type */
-                       DESTROY_IF(public);
-                       continue;
+                       /* 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->current, this->current) ||
+                               verify_trust_chain(this->this, this->current, this->auth,
+                                                                                        TRUE, this->crl, this->ocsp))
+                       {
+                               DBG1(DBG_CFG, "  using trusted certificate \"%D\"",
+                                        this->current->get_subject(this->current));
+                               *cert = this->current;
+                               if (auth)
+                               {
+                                       *auth = this->auth;
+                               }
+                               return TRUE;
+                       }
+                       return FALSE;
                }
-               DESTROY_IF(public);
-               if (this->id && !current->has_subject(current, this->id))
-               {       /* subject requested, but does not match */
-                       continue;
+       }
+       /* try to verify the trust chain for each certificate found */
+       while (this->candidates->enumerate(this->candidates, &this->current))
+       {
+               DBG1(DBG_CFG, "  using certificate \"%D\"",
+                        this->current->get_subject(this->current));
+               if (verify_trust_chain(this->this, this->current, this->auth, FALSE,
+                                                          this->crl, this->ocsp))
+               {
+                       *cert = this->current->get_ref(this->current);
+                       if (auth)
+                       {
+                               *auth = this->auth;
+                       }
+                       return TRUE;
                }
-               *cert = current;
-               return TRUE;
        }
        return FALSE;
 }
 
 /**
- * destroy function for auth_wrapper_enumerator_t
+ * Implements trusted_enumerator_t.destroy
  */
-static void auth_wrapper_enum_destroy(auth_wrapper_enumerator_t *this)
+static void trusted_destroy(trusted_enumerator_t *this)
 {
-       this->inner->destroy(this->inner);
+       DESTROY_IF(this->current);
+       DESTROY_IF(this->auth);
+       DESTROY_IF(this->candidates);
        free(this);
 }
 
 /**
- * implementation of auth_wrapper_t.set.create_cert_enumerator
+ * create an enumerator over trusted certificates and their trustchain
  */
-static enumerator_t *auth_wrapper_create_enumerator(auth_wrapper_t *this,
-                                                                               certificate_type_t cert, key_type_t key,
-                                                                               identification_t *id, bool trusted)
+static enumerator_t *create_trusted_enumerator(private_credential_manager_t *this,
+                                       key_type_t type, identification_t *id, bool crl, bool ocsp)
 {
-       auth_wrapper_enumerator_t *enumerator;
+       trusted_enumerator_t *enumerator = malloc_thing(trusted_enumerator_t);
        
-       if (trusted)
-       {
-               return NULL;
-       }
+       enumerator->public.enumerate = (void*)trusted_enumerate;
+       enumerator->public.destroy = (void*)trusted_destroy;
        
-       enumerator = malloc_thing(auth_wrapper_enumerator_t);
-       enumerator->cert = cert;
-       enumerator->key = key;
+       enumerator->candidates = NULL;
+       enumerator->this = this;
+       enumerator->type = type;
        enumerator->id = id;
-       enumerator->inner = this->auth->create_item_enumerator(this->auth);
-       enumerator->public.enumerate = (void*)auth_wrapper_enum_enumerate;
-       enumerator->public.destroy = (void*)auth_wrapper_enum_destroy;
+       enumerator->crl = crl;
+       enumerator->ocsp = ocsp;
+       enumerator->current = NULL;
+       enumerator->auth = NULL;
+       
        return &enumerator->public;
 }
 
 /**
- * create credential_set wrapper around auth_info_t
+ * enumerator for public keys
  */
-static auth_wrapper_t *auth_wrapper_create(auth_info_t *auth)
-{
-       auth_wrapper_t *this = malloc_thing(auth_wrapper_t);
-       
-       this->auth = auth;
-       this->set.create_private_enumerator = (void*)return_null;
-       this->set.create_cert_enumerator = (void*)auth_wrapper_create_enumerator;
-       this->set.create_shared_enumerator = (void*)return_null;
-       this->set.create_cdp_enumerator = (void*)return_null;
-
-       return this;
-}
+typedef struct {
+       /** implements enumerator_t interface */
+       enumerator_t public;
+       /** enumerator over candidate peer certificates */
+       enumerator_t *inner;
+       /** reference to the credential_manager */
+       private_credential_manager_t *this;
+       /** currently enumerating key */
+       public_key_t *current;
+       /** credset wrapper around auth */
+       auth_info_wrapper_t *wrapper;
+} public_enumerator_t;
 
 /**
- * Get a trusted certificate
+ * Implements public_enumerator_t.enumerate
  */
-static certificate_t *get_trusted_cert(private_credential_manager_t *this,
-                                                                          key_type_t type, identification_t *id,
-                                                                          auth_info_t *auth, bool crl, bool ocsp)
+static bool public_enumerate(public_enumerator_t *this,
+                                                        public_key_t **key, auth_info_t **auth)
 {
-       enumerator_t *enumerator;
-       auth_wrapper_t *wrapper;
-       certificate_t *subject, *issuer, *candidate;
-       public_key_t *public;
-       bool trusted = FALSE;
-       auth_info_t *auth1, *auth2;
-       u_int level = 0;
-       
-       this->mutex->lock(this->mutex);
-       wrapper = auth_wrapper_create(auth);
-       this->sets->insert_first(this->sets, wrapper);
-       
-       /* check if we have a trusted certificate for that peer */
-       auth1 = auth_info_create();
-       subject = get_cert(this, CERT_ANY, type, id, TRUE);
-       if (subject)
-       {
-               if (check_certificate(this, subject, NULL, crl, ocsp, auth1))
-               {
-                       public = subject->get_public_key(subject);
-                       if (public)
-                       {
-                               DBG1(DBG_CFG, "  using trusted certificate \"%D\"",
-                                                            subject->get_subject(subject));
-                               this->sets->remove(this->sets, wrapper, NULL);
-                               free(wrapper);
-                               this->mutex->unlock(this->mutex);
-                               auth->add_item(auth1, AUTHZ_SUBJECT_CERT, subject);
-                               public->destroy(public);
-                               auth->merge(auth, auth1);
-                               auth1->destroy(auth1);
-                               return subject;
-                       }
-               }
-               subject->destroy(subject);
-       }
-       auth1->destroy(auth1);
+       certificate_t *cert;
        
-       /* check for an untrusted certificate */
-       auth1 = auth_info_create();
-       subject = get_cert(this, CERT_ANY, type, id, FALSE);
-       if (!subject)
+       while (this->inner->enumerate(this->inner, &cert, auth))
        {
-               DBG1(DBG_CFG, "no end entity certificate found for \"%D\"", id);
-       }
-       else
-       {
-               issuer = subject;
-               do
-               {
-                       /* look for a trusted certificate */
-                       auth2 = auth_info_create();
-                       enumerator = create_cert_enumerator(this, issuer->get_type(issuer), 
-                                                                       KEY_ANY, issuer->get_issuer(issuer), TRUE);
-                       while (enumerator->enumerate(enumerator, &candidate))
-                       {
-                               if (check_certificate(this, issuer, candidate, crl, ocsp,
-                                                                         issuer == subject ? auth2 : NULL) &&
-                                       check_certificate(this, candidate, NULL, crl, ocsp, NULL))
-                               {
-                                       DBG1(DBG_CFG, "  using trusted root ca certificate \"%D\"",
-                                                candidate->get_subject(candidate));
-                                       issuer = candidate;
-                                       trusted = TRUE;
-                                       auth1->merge(auth1, auth2);
-                                       auth1->add_item(auth1, AUTHZ_CA_CERT, candidate);
-                                       break;
-                               }
-                       }
-                       enumerator->destroy(enumerator);
-                       auth2->destroy(auth2);
-                       if (trusted)
-                       {
-                               break;
-                       }
-                       
-                       /* no trusted certificate found, look for an untrusted */
-                       enumerator = create_cert_enumerator(this, issuer->get_type(issuer), 
-                                                                       KEY_ANY, issuer->get_issuer(issuer), FALSE);
-                       while (enumerator->enumerate(enumerator, &candidate))
-                       {
-                               auth2 = auth_info_create();
-                               if (check_certificate(this, issuer, candidate, crl, ocsp,
-                                                                         issuer == subject ? auth2 : NULL))
-                               {
-                                       if (issuer != subject)
-                                       {
-                                               DBG1(DBG_CFG, "  using intermediate ca certificate \"%D\"",
-                                                        candidate->get_subject(candidate));
-                                               auth1->add_item(auth1, AUTHZ_IM_CERT, candidate);
-                                       }
-                                       else
-                                       {
-                                               DBG1(DBG_CFG, "  using end entity certificate \"%D\"",
-                                                        candidate->get_subject(candidate));
-                                       }
-                                       issuer = candidate;
-                                       auth1->merge(auth1, auth2);
-                                       auth2->destroy(auth2);
-                                       /* check next level */
-                                       break;
-                               }
-                               auth2->destroy(auth2);
-                       }
-                       enumerator->destroy(enumerator);
-               }
-               while (++level < MAX_CA_LEVELS);
-               
-               if (!trusted)
+               DESTROY_IF(this->current);
+               this->current = cert->get_public_key(cert);
+               if (this->current)
                {
-                       subject->destroy(subject);
-                       subject = NULL;
+                       *key = this->current;
+                       return TRUE;
                }
        }
-       this->sets->remove(this->sets, wrapper, NULL);
-       free(wrapper);
-       this->mutex->unlock(this->mutex);
-       if (subject)
-       {
-               auth->add_item(auth, AUTHZ_SUBJECT_CERT, subject);
-               auth->merge(auth, auth1);
-               auth1->destroy(auth1);
-               return subject;
-       }
-       auth1->destroy(auth1);
-       return NULL;
+       return FALSE;
 }
 
 /**
- * Implementation of credential_manager_t.get_public.
+ * Implements public_enumerator_t.destroy
  */
-static public_key_t *get_public(private_credential_manager_t *this,
-                                                               key_type_t type, identification_t *id,
-                                                               auth_info_t *auth)
+static void public_destroy(public_enumerator_t *this)
 {
-       public_key_t *public;
-       certificate_t *cert;
-       
-       cert = get_trusted_cert(this, type, id, auth, TRUE, TRUE);
-       if (cert)
+       if (this->wrapper)
        {
-               public = cert->get_public_key(cert);
-               cert->destroy(cert);
-               return public;
+               this->this->sets->remove(this->this->sets, this->wrapper, NULL);
+               this->this->mutex->unlock(this->this->mutex);
+               this->wrapper->destroy(this->wrapper);
        }
-       return NULL;
+       DESTROY_IF(this->current);
+       this->inner->destroy(this->inner);
+       free(this);
 }
 
 /**
- * Get the issuing certificate of a subject certificate
+ * Implementation of credential_manager_t.create_public_enumerator.
  */
-static certificate_t *get_issuer_cert(private_credential_manager_t *this,
-                                                                         certificate_t *subject)
+static enumerator_t* create_public_enumerator(private_credential_manager_t *this,
+                                               key_type_t type, identification_t *id, auth_info_t *auth)
 {
-       enumerator_t *enumerator;
-       certificate_t *issuer = NULL, *candidate;
+       public_enumerator_t *enumerator = malloc_thing(public_enumerator_t);
        
-       enumerator = create_cert_enumerator(this, subject->get_type(subject), KEY_ANY, 
-                                                                               subject->get_issuer(subject), FALSE);
-       while (enumerator->enumerate(enumerator, &candidate))
+       enumerator->public.enumerate = (void*)public_enumerate;
+       enumerator->public.destroy = (void*)public_destroy;
+       enumerator->inner = create_trusted_enumerator(this, type, id, TRUE, TRUE);
+       enumerator->this = this;
+       enumerator->current = NULL;
+       enumerator->wrapper = NULL;
+       this->mutex->lock(this->mutex);
+       if (auth)
        {
-               if (subject->issued_by(subject, candidate, FALSE))
-               {
-                       issuer = candidate->get_ref(candidate);
-                       break;
-               }
+               enumerator->wrapper = auth_info_wrapper_create(auth);
+               this->sets->remove(this->sets, this->cache, NULL);
+               this->sets->insert_first(this->sets, enumerator->wrapper);
+               this->sets->insert_first(this->sets, this->cache);
        }
-       enumerator->destroy(enumerator);
-       return issuer;
+       return &enumerator->public;
 }
 
 /**
@@ -1232,6 +1158,25 @@ static bool auth_contains_cacert(auth_info_t *auth, certificate_t *cert)
                        found = TRUE;
                        break;
                }
+               if (type == AUTHN_CA_CERT_KEYID)
+               {
+                       public_key_t *public;
+                       identification_t *certid, *keyid;
+                       
+                       public = cert->get_public_key(cert);
+                       if (public)
+                       {
+                               keyid = (identification_t*)value;
+                               certid = public->get_id(public, keyid->get_type(keyid));
+                               if (certid && certid->equals(certid, keyid))
+                               {
+                                       public->destroy(public);
+                                       found = TRUE;
+                                       break;
+                               }
+                               public->destroy(public);
+                       }
+               }
        }
        enumerator->destroy(enumerator);
        return found;
@@ -1255,7 +1200,6 @@ static auth_info_t *build_trustchain(private_credential_manager_t *this,
                trustchain->add_item(trustchain, AUTHZ_SUBJECT_CERT, subject);
                return trustchain;
        }
-       
        current = subject->get_ref(subject);
        while (TRUE)
        {
@@ -1273,7 +1217,7 @@ static auth_info_t *build_trustchain(private_credential_manager_t *this,
                {
                        trustchain->add_item(trustchain, AUTHZ_IM_CERT, current);
                }
-               issuer = get_issuer_cert(this, current);
+               issuer = get_issuer_cert(this, current, FALSE);
                if (!issuer || issuer->equals(issuer, current) || level > MAX_CA_LEVELS)
                {
                        DESTROY_IF(issuer);
@@ -1337,10 +1281,10 @@ static private_key_t *get_private(private_credential_manager_t *this,
        }
        
        this->mutex->lock(this->mutex);
-       /* get all available end entity certificates for ourself */
+       /* try to build a trustchain 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)
                {
@@ -1352,14 +1296,41 @@ static private_key_t *get_private(private_credential_manager_t *this,
                                break;
                        }
                        private->destroy(private);
+                       private = NULL;
                }
        }
        enumerator->destroy(enumerator);
+       /* if no valid trustchain was found, fall back to the first usable cert */
+       if (!private)
+       {
+               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)
+                       {
+                               auth->add_item(auth, AUTHZ_SUBJECT_CERT, cert);
+                               break;
+                       }
+               }
+               enumerator->destroy(enumerator);
+       }
        this->mutex->unlock(this->mutex);
        return private;
 }
 
 /**
+ * Implementation of credential_manager_t.flush_cache.
+ */
+static void flush_cache(private_credential_manager_t *this,
+                                               certificate_type_t type)
+{
+       this->mutex->lock(this->mutex);
+       this->cache->flush(this->cache, type);
+       this->mutex->unlock(this->mutex);
+}
+
+/**
  * Implementation of credential_manager_t.add_set.
  */
 static void add_set(private_credential_manager_t *this,
@@ -1384,7 +1355,9 @@ static void remove_set(private_credential_manager_t *this, credential_set_t *set
  */
 static void destroy(private_credential_manager_t *this)
 {
+       this->sets->remove(this->sets, this->cache, NULL);
        this->sets->destroy(this->sets);
+       this->cache->destroy(this->cache);
        this->mutex->destroy(this->mutex);
        free(this);
 }
@@ -1402,12 +1375,15 @@ credential_manager_t *credential_manager_create()
        this->public.get_cert = (certificate_t *(*)(credential_manager_t *this,certificate_type_t cert, key_type_t key,identification_t *, bool))get_cert;
        this->public.get_shared = (shared_key_t *(*)(credential_manager_t *this,shared_key_type_t type,identification_t *me, identification_t *other))get_shared;
        this->public.get_private = (private_key_t*(*)(credential_manager_t*, key_type_t type, identification_t *, auth_info_t*))get_private;
-       this->public.get_public = (public_key_t*(*)(credential_manager_t*, key_type_t type, identification_t *, auth_info_t*))get_public;
+       this->public.create_public_enumerator = (enumerator_t*(*)(credential_manager_t*, key_type_t type, identification_t *id, auth_info_t *aut))create_public_enumerator;
+       this->public.flush_cache = (void(*)(credential_manager_t*, certificate_type_t type))flush_cache;
        this->public.add_set = (void(*)(credential_manager_t*, credential_set_t *set))add_set;
        this->public.remove_set = (void(*)(credential_manager_t*, credential_set_t *set))remove_set;
        this->public.destroy = (void(*)(credential_manager_t*))destroy;
        
        this->sets = linked_list_create();
+       this->cache = cert_cache_create();
+       this->sets->insert_first(this->sets, this->cache);
        this->mutex = mutex_create(MUTEX_RECURSIVE);
        
        return &this->public;