fixed crash if crl fetching fails
[strongswan.git] / src / charon / credentials / credential_manager.c
index 7c49d39..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
  */
@@ -357,15 +356,15 @@ static certificate_t *fetch_ocsp(private_credential_manager_t *this, char *url,
 /**
  * check the signature of an OCSP response 
  */
-static bool check_ocsp_response(private_credential_manager_t *this,
-                                                           ocsp_response_t *response)
+static bool verify_ocsp(private_credential_manager_t *this, 
+                                               ocsp_response_t *response)
 {
        certificate_t *issuer, *subject;
        identification_t *responder;
-       auth_info_t *auth;
        ocsp_response_wrapper_t *wrapper;
+       enumerator_t *enumerator;
+       bool verified = FALSE;
 
-       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);
@@ -373,25 +372,20 @@ static bool check_ocsp_response(private_credential_manager_t *this,
        
        subject = &response->certificate;
        responder = subject->get_issuer(subject);
-       issuer = get_trusted_cert(this, KEY_ANY, responder, auth, FALSE, FALSE);
+       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);
-       auth->destroy(auth);
-
-       if (!issuer)
-       {
-               DBG1(DBG_CFG, "OCSP response verification failed, responder cert missing");
-               return FALSE;
-       }
-       if (!this->cache->issued_by(this->cache, subject, issuer))
-       {
-               DBG1(DBG_CFG, "OCSP response verification failed");
-               issuer->destroy(issuer);
-               return FALSE;
-       }
-       issuer->destroy(issuer);
-       return TRUE;
+       return verified;
 }
 
 /**
@@ -409,8 +403,9 @@ static certificate_t *get_better_ocsp(private_credential_manager_t *this,
        response = (ocsp_response_t*)cand;
 
        /* check ocsp signature */
-       if (!check_ocsp_response(this, response))
+       if (!verify_ocsp(this, response))
        {
+               DBG1(DBG_CFG, "OCSP response verification failed");
                cand->destroy(cand);
                return best;
        }
@@ -423,6 +418,7 @@ static certificate_t *get_better_ocsp(private_credential_manager_t *this,
                        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 */
@@ -481,7 +477,7 @@ static cert_validation_t check_ocsp(private_credential_manager_t *this,
                best = get_better_ocsp(this, current, best, subject, issuer, &valid);
                if (best && valid != VALIDATION_STALE)
                {
-                       DBG1(DBG_CFG, "  used cached ocsp response");
+                       DBG1(DBG_CFG, "found cached ocsp response");
                        break;
                }
        }
@@ -552,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? */
@@ -562,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;
 }
 
 /**
@@ -610,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);
@@ -628,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))
-                               {
-                                       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);
 
-       /* 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_STALE : 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;
 }
 
@@ -904,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
                {
@@ -922,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
                        {
@@ -957,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 we find a trusted self signed certificate, we just accept it */
-               if (this->cache->issued_by(this->cache, subject, subject))
-               {
-                       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;
 }
 
 /**
@@ -1278,7 +1375,7 @@ 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;