fixed crash if crl fetching fails
[strongswan.git] / src / charon / credentials / credential_manager.c
index 34a801e..ac97f73 100644 (file)
@@ -303,10 +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);
 
+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
  */
@@ -331,6 +330,7 @@ static certificate_t *fetch_ocsp(private_credential_manager_t *this, char *url,
        request->destroy(request);
 
        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",
@@ -350,50 +350,108 @@ static certificate_t *fetch_ocsp(private_credential_manager_t *this, char *url,
                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;
 
-       /* verify the signature of the ocsp response */
+       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))
        {
-               certificate_t *issuer_cert;
-               identification_t *responder;
-               auth_info_t *auth;
-               ocsp_response_wrapper_t *wrapper;
-               auth = auth_info_create();
-               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);
-               responder = response->get_issuer(response);
-               DBG1(DBG_CFG, "  ocsp signer is \"%D\"", responder);
-               issuer_cert = get_trusted_cert(this, KEY_ANY, responder, auth, FALSE, FALSE);
-               this->sets->remove(this->sets, wrapper, NULL);
-               wrapper->destroy(wrapper);
-               auth->destroy(auth);
-
-               if (!issuer_cert)
+               if (this->cache->issued_by(this->cache, subject, issuer))
                {
-                       DBG1(DBG_CFG, "ocsp response untrusted: no signer certificate found");
-                       response->destroy(response);
-                       return NULL;
+                       verified = TRUE;
+                       break;
                }
-               if (this->cache->issued_by(this->cache, response, issuer_cert))
+       }
+       enumerator->destroy(enumerator);
+       
+       this->sets->remove(this->sets, wrapper, NULL);
+       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)
+{
+       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))
+       {
+               DESTROY_IF(best);
+               best = cand;
+               if (best->get_validity(best, NULL, NULL, &valid_until))
                {
-                       DBG1(DBG_CFG, "  ocsp response correctly signed by \"%D\"",
-                                                  issuer_cert->get_subject(issuer_cert));
-                       issuer_cert->destroy(issuer_cert);
+                       DBG1(DBG_CFG, "  ocsp response is valid: until %#T", &valid_until);
+                       *valid = VALIDATION_GOOD;
                }
                else
                {
-                       DBG1(DBG_CFG, "ocsp response not accepted from \"%D\"",
-                                                  issuer_cert->get_subject(issuer_cert));
-                       issuer_cert->destroy(issuer_cert);
-                       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;
 }
 
 /**
@@ -403,92 +461,49 @@ 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)
        {
-               time_t nextUpdate;
-
-               stale = !best_cert->get_validity(best_cert, NULL, NULL, &nextUpdate);
-               DBG1(DBG_CFG, "cached ocsp response is %s %#T",
-                                          stale? "stale: since":"valid: until",
-                                          &nextUpdate, FALSE );
+               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))
-                               {
-                                       time_t nextUpdate;
-
-                                       DESTROY_IF(best_cert);
-                                       best_cert = cert;
-                                       stale = !best_cert->get_validity(best_cert, NULL, NULL, &nextUpdate);
-                                       DBG1(DBG_CFG, "ocsp response is %s %#T",
-                                                                  stale? "stale: since":"valid: until",
-                                                                  &nextUpdate, FALSE );
-                                       if (!stale)
-                                       {
-                                               break;
-                                       }
-                               }
-                               else
+                               best = get_better_ocsp(this, current, best, subject, issuer, &valid);
+                               if (best && valid != VALIDATION_STALE)
                                {
-                                       cert->destroy(cert);
+                                       break;
                                }
                        }
                }
@@ -497,76 +512,34 @@ static cert_validation_t check_ocsp(private_credential_manager_t *this,
        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))
+                               best = get_better_ocsp(this, current, best, subject, issuer, &valid);
+                               if (best && valid != VALIDATION_STALE)
                                {
-                                       time_t nextUpdate;
-
-                                       DESTROY_IF(best_cert);
-                                       best_cert = cert;
-                                       stale = !best_cert->get_validity(best_cert, NULL, NULL, &nextUpdate);
-                                       DBG1(DBG_CFG, "ocsp response is %s %#T",
-                                                                  stale? "stale: since":"valid: until",
-                                                                  &nextUpdate, FALSE );
-                                       if (!stale)
-                                       {
-                                               break;
-                                       }
-                               }
-                               else
-                               {
-                                       cert->destroy(cert);
+                                       break;
                                }
                        }
                }
                enumerator->destroy(enumerator);
        }
-
-       /* 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;
 }
 
@@ -575,7 +548,7 @@ 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? */
@@ -585,45 +558,102 @@ static certificate_t* fetch_crl(private_credential_manager_t *this, char *url)
                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");
                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;
        
-       /* verify the signature of the fetched crl */
+       enumerator = create_trusted_enumerator(this, KEY_ANY, crl->get_issuer(crl),
+                                                                                  FALSE, FALSE);
+       while (enumerator->enumerate(enumerator, &issuer, NULL))
        {
-               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 (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;
 
-               if (!issuer_cert)
+       /* 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, "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;
                }
-               
-               if (this->cache->issued_by(this->cache, crl_cert, issuer_cert))
+       }
+       enumerator->destroy(enumerator);
+
+       /* 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 correctly signed by \"%D\"",
-                                                  issuer_cert->get_subject(issuer_cert));
-                       issuer_cert->destroy(issuer_cert);
+                       DBG1(DBG_CFG, "  crl is valid: until %#T", &valid_until);
+                       *valid = VALIDATION_GOOD;
                }
                else
                {
-                       DBG1(DBG_CFG, "crl not accepted from \"%D\"",
-                                                  issuer_cert->get_subject(issuer_cert));
-                       issuer_cert->destroy(issuer_cert);
-                       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;
 }
 
 /**
@@ -633,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);
@@ -651,146 +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)
-       {
-               time_t nextUpdate;
-
-               stale = !best_cert->get_validity(best_cert, NULL, NULL, &nextUpdate);
-               DBG1(DBG_CFG, "cached crl is %s %#T",
-                                          stale? "stale: since":"valid: until",
-                                          &nextUpdate, FALSE );
-       }
-
-       /* fallback to fetching crls from cdps defined in ca info sections */
-       if (stale && keyid)
+       /* fallback to fetching crls from credential sets cdps */
+       if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
        {
-               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)
                                {
-                                       time_t nextUpdate;
-
-                                       DESTROY_IF(best_cert);
-                                       best_cert = cert;
-                                       stale = !best_cert->get_validity(best_cert, NULL, NULL, &nextUpdate);
-                                       DBG1(DBG_CFG, "fetched crl is %s %#T",
-                                                                  stale? "stale: since":"valid: until",
-                                                                  &nextUpdate, FALSE );
-                                       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))
-                               {
-                                       time_t nextUpdate;
-
-                                       DESTROY_IF(best_cert);
-                                       best_cert = cert;
-                                       stale = !best_cert->get_validity(best_cert, NULL, NULL, &nextUpdate);
-                                       DBG1(DBG_CFG, "fetched crl is %s %#T",
-                                                                  stale? "stale: since":"valid: until",
-                                                                  &nextUpdate, FALSE );
-                                       if (!stale)
-                                       {
-                                               break;
-                                       }
-                               }
-                               else
+                               best = get_better_crl(this, current, best, subject, issuer, &valid);
+                               if (best && valid != VALIDATION_STALE)
                                {
-                                       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;
 }
 
@@ -831,8 +788,10 @@ static bool check_certificate(private_credential_manager_t *this,
                                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:
-                               case VALIDATION_UNKNOWN:
                                        DBG1(DBG_CFG, "ocsp check failed, fallback to crl");
                                        break;
                        }
@@ -847,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;
                        }
                }
@@ -927,10 +884,20 @@ static bool verify_trust_chain(private_credential_manager_t *this,
                issuer = get_issuer_cert(this, current, TRUE);
                if (issuer)
                {
-                       auth->add_item(auth, AUTHZ_CA_CERT, issuer);    
-                       DBG1(DBG_CFG, "  using trusted ca certificate \"%D\"",
-                                issuer->get_subject(issuer));
-                       trusted = TRUE;
+                       /* 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
                {
@@ -945,8 +912,8 @@ static bool verify_trust_chain(private_credential_manager_t *this,
                                        break;
                                }
                                auth->add_item(auth, AUTHZ_IM_CERT, issuer);
-                               DBG1(DBG_CFG, "  using untrusted ca certificate \"%D\"",
-                                        issuer->get_subject(issuer));
+                               DBG1(DBG_CFG, "  using untrusted intermediate certificate "
+                                        "\"%D\"", issuer->get_subject(issuer));
                        }
                        else
                        {
@@ -980,90 +947,197 @@ static bool verify_trust_chain(private_credential_manager_t *this,
 }
 
 /**
- * Get a trusted certificate by verifying the trust chain
+ * enumerator for trusted certificates
  */
-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)
+typedef struct {
+       /** implements enumerator_t interface */
+       enumerator_t public;
+       /** 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;
+       /** 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;
+
+/**
+ * Implements trusted_enumerator_t.enumerate
+ */
+static bool trusted_enumerate(trusted_enumerator_t *this,
+                                                         certificate_t **cert, auth_info_t **auth)
 {
-       certificate_t *subject, *current;
-       enumerator_t *enumerator;
+       DESTROY_IF(this->current);
+       DESTROY_IF(this->auth);
+       this->auth = auth_info_create();
        
-       /* check if we have a trusted certificate for that peer */
-       subject = get_pretrusted_cert(this, type, id);
-       if (subject)
+       if (!this->candidates)
        {
-
-               if (subject->issued_by(subject, subject, TRUE))
-               {
-                       DBG1(DBG_CFG, "  using trusted self-signed certificate \"%D\"",
-                                subject->get_subject(subject));
-                       return subject;
-               }
-
-               /* if we find a trusted certificate, we accept it. However, in order
-         * to fulfill authorization rules, we try to build the trust chain
-                * anyway.
-                */
-               if (verify_trust_chain(this, subject, auth, TRUE, crl, ocsp))
+               /* 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)
                {
-                       DBG1(DBG_CFG, "  using trusted certificate \"%D\"",
-                                subject->get_subject(subject));
-                       return subject;
+                       /* 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;
                }
-               subject->destroy(subject);
        }
-       
-       subject = NULL;
        /* try to verify the trust chain for each certificate found */
-       enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE);
-       while (enumerator->enumerate(enumerator, &current))
+       while (this->candidates->enumerate(this->candidates, &this->current))
        {
                DBG1(DBG_CFG, "  using certificate \"%D\"",
-                        current->get_subject(current));
-               if (verify_trust_chain(this, current, auth, FALSE, crl, ocsp))
+                        this->current->get_subject(this->current));
+               if (verify_trust_chain(this->this, this->current, this->auth, FALSE,
+                                                          this->crl, this->ocsp))
                {
-                       subject = current->get_ref(current);
-                       break;
+                       *cert = this->current->get_ref(this->current);
+                       if (auth)
+                       {
+                               *auth = this->auth;
+                       }
+                       return TRUE;
                }
        }
-       enumerator->destroy(enumerator);
+       return FALSE;
+}
+
+/**
+ * Implements trusted_enumerator_t.destroy
+ */
+static void trusted_destroy(trusted_enumerator_t *this)
+{
+       DESTROY_IF(this->current);
+       DESTROY_IF(this->auth);
+       DESTROY_IF(this->candidates);
+       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 crl, bool ocsp)
+{
+       trusted_enumerator_t *enumerator = malloc_thing(trusted_enumerator_t);
        
-       if (!subject)
+       enumerator->public.enumerate = (void*)trusted_enumerate;
+       enumerator->public.destroy = (void*)trusted_destroy;
+       
+       enumerator->candidates = NULL;
+       enumerator->this = this;
+       enumerator->type = type;
+       enumerator->id = id;
+       enumerator->crl = crl;
+       enumerator->ocsp = ocsp;
+       enumerator->current = NULL;
+       enumerator->auth = NULL;
+       
+       return &enumerator->public;
+}
+
+/**
+ * enumerator for public keys
+ */
+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;
+
+/**
+ * Implements public_enumerator_t.enumerate
+ */
+static bool public_enumerate(public_enumerator_t *this,
+                                                        public_key_t **key, auth_info_t **auth)
+{
+       certificate_t *cert;
+       
+       while (this->inner->enumerate(this->inner, &cert, auth))
        {
-               DBG1(DBG_CFG, "no trusted certificate found for '%D'", id);
+               DESTROY_IF(this->current);
+               this->current = cert->get_public_key(cert);
+               if (this->current)
+               {
+                       *key = this->current;
+                       return TRUE;
+               }
        }
-       return subject;
+       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 = NULL;
-       certificate_t *cert;
-       auth_info_wrapper_t *wrapper;
+       if (this->wrapper)
+       {
+               this->this->sets->remove(this->this->sets, this->wrapper, NULL);
+               this->this->mutex->unlock(this->this->mutex);
+               this->wrapper->destroy(this->wrapper);
+       }
+       DESTROY_IF(this->current);
+       this->inner->destroy(this->inner);
+       free(this);
+}
+
+/**
+ * Implementation of credential_manager_t.create_public_enumerator.
+ */
+static enumerator_t* create_public_enumerator(private_credential_manager_t *this,
+                                               key_type_t type, identification_t *id, auth_info_t *auth)
+{
+       public_enumerator_t *enumerator = malloc_thing(public_enumerator_t);
        
-       wrapper = auth_info_wrapper_create(auth);
+       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);
-       this->sets->remove(this->sets, this->cache, NULL);
-       this->sets->insert_first(this->sets, wrapper);
-       this->sets->insert_first(this->sets, this->cache);
-       
-       cert = get_trusted_cert(this, type, id, auth, TRUE, TRUE);
-       if (cert)
+       if (auth)
        {
-               public = cert->get_public_key(cert);
-               cert->destroy(cert);
+               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);
        }
-       
-       this->sets->remove(this->sets, wrapper, NULL);
-       wrapper->destroy(wrapper);
-       this->mutex->unlock(this->mutex);
-       return public;
+       return &enumerator->public;
 }
 
 /**
@@ -1246,6 +1320,17 @@ static private_key_t *get_private(private_credential_manager_t *this,
 }
 
 /**
+ * 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,
@@ -1290,7 +1375,8 @@ 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;