/* * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include "credential_manager.h" #include #include #include #include #include #include #include #include #include /** * Maximum length of a certificate trust chain */ #define MAX_TRUST_PATH_LEN 7 typedef struct private_credential_manager_t private_credential_manager_t; /** * private data of credential_manager */ struct private_credential_manager_t { /** * public functions */ credential_manager_t public; /** * list of credential sets */ linked_list_t *sets; /** * thread local set of credentials, linked_list_t with credential_set_t's */ thread_value_t *local_sets; /** * trust relationship and certificate cache */ cert_cache_t *cache; /** * certificates queued for persistent caching */ linked_list_t *cache_queue; /** * list of certificate validators, cert_validator_t */ linked_list_t *validators; /** * read-write lock to sets list */ rwlock_t *lock; /** * mutex for cache queue */ mutex_t *queue_mutex; }; /** data to pass to create_private_enumerator */ typedef struct { private_credential_manager_t *this; key_type_t type; identification_t* keyid; } private_data_t; /** data to pass to create_cert_enumerator */ typedef struct { private_credential_manager_t *this; certificate_type_t cert; key_type_t key; identification_t *id; bool trusted; } cert_data_t; /** data to pass to create_cdp_enumerator */ typedef struct { private_credential_manager_t *this; certificate_type_t type; identification_t *id; } cdp_data_t; /** data to pass to create_shared_enumerator */ typedef struct { private_credential_manager_t *this; shared_key_type_t type; identification_t *me; identification_t *other; } shared_data_t; /** enumerator over local and global sets */ typedef struct { /** implements enumerator_t */ enumerator_t public; /** enumerator over global sets */ enumerator_t *global; /** enumerator over local sets */ enumerator_t *local; } sets_enumerator_t; METHOD(enumerator_t, sets_enumerate, bool, sets_enumerator_t *this, credential_set_t **set) { if (this->global) { if (this->global->enumerate(this->global, set)) { return TRUE; } /* end of global sets, look for local */ this->global->destroy(this->global); this->global = NULL; } if (this->local) { return this->local->enumerate(this->local, set); } return FALSE; } METHOD(enumerator_t, sets_destroy, void, sets_enumerator_t *this) { DESTROY_IF(this->global); DESTROY_IF(this->local); free(this); } /** * create an enumerator over both, global and local sets */ static enumerator_t *create_sets_enumerator(private_credential_manager_t *this) { sets_enumerator_t *enumerator; linked_list_t *local; INIT(enumerator, .public = { .enumerate = (void*)_sets_enumerate, .destroy = _sets_destroy, }, .global = this->sets->create_enumerator(this->sets), ); local = this->local_sets->get(this->local_sets); if (local) { enumerator->local = local->create_enumerator(local); } return &enumerator->public; } /** * cleanup function for cert data */ static void destroy_cert_data(cert_data_t *data) { data->this->lock->unlock(data->this->lock); free(data); } /** * enumerator constructor for certificates */ static enumerator_t *create_cert(credential_set_t *set, cert_data_t *data) { return set->create_cert_enumerator(set, data->cert, data->key, data->id, data->trusted); } METHOD(credential_manager_t, create_cert_enumerator, enumerator_t*, private_credential_manager_t *this, certificate_type_t certificate, key_type_t key, identification_t *id, bool trusted) { cert_data_t *data = malloc_thing(cert_data_t); data->this = this; data->cert = certificate; data->key = key; data->id = id; data->trusted = trusted; this->lock->read_lock(this->lock); return enumerator_create_nested(create_sets_enumerator(this), (void*)create_cert, data, (void*)destroy_cert_data); } METHOD(credential_manager_t, get_cert, certificate_t*, private_credential_manager_t *this, certificate_type_t cert, key_type_t key, identification_t *id, bool trusted) { certificate_t *current, *found = NULL; enumerator_t *enumerator; enumerator = create_cert_enumerator(this, cert, key, id, trusted); if (enumerator->enumerate(enumerator, ¤t)) { /* TODO: best match? order by keyid, subject, sualtname */ found = current->get_ref(current); } enumerator->destroy(enumerator); return found; } /** * cleanup function for cdp data */ static void destroy_cdp_data(cdp_data_t *data) { data->this->lock->unlock(data->this->lock); free(data); } /** * enumerator constructor for CDPs */ static enumerator_t *create_cdp(credential_set_t *set, cdp_data_t *data) { return set->create_cdp_enumerator(set, data->type, data->id); } METHOD(credential_manager_t, create_cdp_enumerator, enumerator_t*, private_credential_manager_t *this, certificate_type_t type, identification_t *id) { cdp_data_t *data; INIT(data, .this = this, .type = type, .id = id, ); this->lock->read_lock(this->lock); return enumerator_create_nested(create_sets_enumerator(this), (void*)create_cdp, data, (void*)destroy_cdp_data); } /** * cleanup function for private data */ static void destroy_private_data(private_data_t *data) { data->this->lock->unlock(data->this->lock); free(data); } /** * enumerator constructor for private keys */ static enumerator_t *create_private(credential_set_t *set, private_data_t *data) { return set->create_private_enumerator(set, data->type, data->keyid); } /** * Create an enumerator over private keys */ static enumerator_t *create_private_enumerator( private_credential_manager_t *this, key_type_t key, identification_t *keyid) { private_data_t *data; INIT(data, .this = this, .type = key, .keyid = keyid, ); this->lock->read_lock(this->lock); return enumerator_create_nested(create_sets_enumerator(this), (void*)create_private, data, (void*)destroy_private_data); } /** * Look up a private key by its key identifier */ static private_key_t* get_private_by_keyid(private_credential_manager_t *this, key_type_t key, identification_t *keyid) { private_key_t *found = NULL; enumerator_t *enumerator; enumerator = create_private_enumerator(this, key, keyid); if (enumerator->enumerate(enumerator, &found)) { found->get_ref(found); } enumerator->destroy(enumerator); return found; } /** * cleanup function for shared data */ static void destroy_shared_data(shared_data_t *data) { data->this->lock->unlock(data->this->lock); free(data); } /** * enumerator constructor for shared keys */ static enumerator_t *create_shared(credential_set_t *set, shared_data_t *data) { return set->create_shared_enumerator(set, data->type, data->me, data->other); } METHOD(credential_manager_t, create_shared_enumerator, enumerator_t*, private_credential_manager_t *this, shared_key_type_t type, identification_t *me, identification_t *other) { shared_data_t *data; INIT(data, .this = this, .type = type, .me = me, .other = other, ); this->lock->read_lock(this->lock); return enumerator_create_nested(create_sets_enumerator(this), (void*)create_shared, data, (void*)destroy_shared_data); } METHOD(credential_manager_t, get_shared, shared_key_t*, private_credential_manager_t *this, shared_key_type_t type, identification_t *me, identification_t *other) { shared_key_t *current, *found = NULL; id_match_t *best_me = ID_MATCH_NONE, *best_other = ID_MATCH_NONE; id_match_t *match_me, *match_other; enumerator_t *enumerator; enumerator = create_shared_enumerator(this, type, me, other); while (enumerator->enumerate(enumerator, ¤t, &match_me, &match_other)) { if (match_other > best_other || (match_other == best_other && match_me > best_me)) { DESTROY_IF(found); found = current->get_ref(current); best_me = match_me; best_other = match_other; } } enumerator->destroy(enumerator); return found; } METHOD(credential_manager_t, add_local_set, void, private_credential_manager_t *this, credential_set_t *set) { linked_list_t *sets; sets = this->local_sets->get(this->local_sets); if (!sets) { /* first invocation */ sets = linked_list_create(); this->local_sets->set(this->local_sets, sets); } sets->insert_last(sets, set); } METHOD(credential_manager_t, remove_local_set, void, private_credential_manager_t *this, credential_set_t *set) { linked_list_t *sets; sets = this->local_sets->get(this->local_sets); sets->remove(sets, set, NULL); } METHOD(credential_manager_t, cache_cert, void, private_credential_manager_t *this, certificate_t *cert) { credential_set_t *set; enumerator_t *enumerator; if (this->lock->try_write_lock(this->lock)) { enumerator = this->sets->create_enumerator(this->sets); while (enumerator->enumerate(enumerator, &set)) { set->cache_cert(set, cert); } enumerator->destroy(enumerator); this->lock->unlock(this->lock); } else { /* we can't cache now as other threads are active, queue for later */ this->queue_mutex->lock(this->queue_mutex); this->cache_queue->insert_last(this->cache_queue, cert->get_ref(cert)); this->queue_mutex->unlock(this->queue_mutex); } } /** * Try to cache certificates queued for caching */ static void cache_queue(private_credential_manager_t *this) { credential_set_t *set; certificate_t *cert; enumerator_t *enumerator; this->queue_mutex->lock(this->queue_mutex); if (this->cache_queue->get_count(this->cache_queue) > 0 && this->lock->try_write_lock(this->lock)) { while (this->cache_queue->remove_last(this->cache_queue, (void**)&cert) == SUCCESS) { enumerator = this->sets->create_enumerator(this->sets); while (enumerator->enumerate(enumerator, &set)) { set->cache_cert(set, cert); } enumerator->destroy(enumerator); cert->destroy(cert); } this->lock->unlock(this->lock); } this->queue_mutex->unlock(this->queue_mutex); } /** * check a certificate for its lifetime */ static bool check_certificate(private_credential_manager_t *this, certificate_t *subject, certificate_t *issuer, bool online, int pathlen, auth_cfg_t *auth) { time_t not_before, not_after; cert_validator_t *validator; enumerator_t *enumerator; if (!subject->get_validity(subject, NULL, ¬_before, ¬_after)) { DBG1(DBG_CFG, "subject certificate invalid (valid from %T to %T)", ¬_before, FALSE, ¬_after, FALSE); return FALSE; } if (!issuer->get_validity(issuer, NULL, ¬_before, ¬_after)) { DBG1(DBG_CFG, "issuer certificate invalid (valid from %T to %T)", ¬_before, FALSE, ¬_after, FALSE); return FALSE; } if (issuer->get_type(issuer) == CERT_X509 && subject->get_type(subject) == CERT_X509) { int pathlen_constraint; x509_t *x509; /* check path length constraint */ x509 = (x509_t*)issuer; pathlen_constraint = x509->get_pathLenConstraint(x509); if (pathlen_constraint != X509_NO_PATH_LEN_CONSTRAINT && pathlen > pathlen_constraint) { DBG1(DBG_CFG, "path length of %d violates constraint of %d", pathlen, pathlen_constraint); return FALSE; } } enumerator = this->validators->create_enumerator(this->validators); while (enumerator->enumerate(enumerator, &validator)) { if (!validator->validate(validator, subject, issuer, online, pathlen, auth)) { enumerator->destroy(enumerator); return FALSE; } } enumerator->destroy(enumerator); return TRUE; } /** * Get a trusted certificate from a credential 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_cfg_t *result, bool trusted, bool online) { certificate_t *current, *issuer; auth_cfg_t *auth; int pathlen; auth = auth_cfg_create(); current = subject->get_ref(subject); for (pathlen = 0; pathlen <= MAX_TRUST_PATH_LEN; pathlen++) { 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(auth, AUTH_RULE_CA_CERT, issuer->get_ref(issuer)); DBG1(DBG_CFG, " using trusted ca certificate \"%Y\"", issuer->get_subject(issuer)); trusted = TRUE; } else { auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer)); DBG1(DBG_CFG, " using trusted intermediate ca certificate " "\"%Y\"", 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 \"%Y\" is not trusted", current->get_subject(current)); issuer->destroy(issuer); break; } auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer)); DBG1(DBG_CFG, " using untrusted intermediate certificate " "\"%Y\"", issuer->get_subject(issuer)); } else { DBG1(DBG_CFG, "no issuer certificate found for \"%Y\"", current->get_subject(current)); break; } } if (!check_certificate(this, current, issuer, online, pathlen, current == subject ? auth : NULL)) { trusted = FALSE; issuer->destroy(issuer); break; } current->destroy(current); current = issuer; if (trusted) { DBG1(DBG_CFG, " reached self-signed root ca with a path length of %d", pathlen); break; } } current->destroy(current); if (pathlen > MAX_TRUST_PATH_LEN) { DBG1(DBG_CFG, "maximum path length of %d exceeded", MAX_TRUST_PATH_LEN); } if (trusted) { result->merge(result, auth, FALSE); } auth->destroy(auth); return trusted; } /** * enumerator for trusted certificates */ 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/OCSP checking */ bool online; /** pretrusted certificate we have served at first invocation */ certificate_t *pretrusted; /** currently enumerating auth config */ auth_cfg_t *auth; } trusted_enumerator_t; METHOD(enumerator_t, trusted_enumerate, bool, trusted_enumerator_t *this, certificate_t **cert, auth_cfg_t **auth) { certificate_t *current; DESTROY_IF(this->auth); this->auth = auth_cfg_create(); if (!this->candidates) { /* 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->pretrusted = get_pretrusted_cert(this->this, this->type, this->id); if (this->pretrusted) { /* if we find a trusted self signed certificate, we just accept it. * However, in order to fulfill authorization rules, we try to build * the trust chain if it is not self signed */ if (this->this->cache->issued_by(this->this->cache, this->pretrusted, this->pretrusted) || verify_trust_chain(this->this, this->pretrusted, this->auth, TRUE, this->online)) { this->auth->add(this->auth, AUTH_RULE_SUBJECT_CERT, this->pretrusted->get_ref(this->pretrusted)); DBG1(DBG_CFG, " using trusted certificate \"%Y\"", this->pretrusted->get_subject(this->pretrusted)); *cert = this->pretrusted; if (auth) { *auth = this->auth; } return TRUE; } } } /* try to verify the trust chain for each certificate found */ while (this->candidates->enumerate(this->candidates, ¤t)) { if (this->pretrusted && this->pretrusted->equals(this->pretrusted, current)) { /* skip pretrusted certificate we already served */ continue; } DBG1(DBG_CFG, " using certificate \"%Y\"", current->get_subject(current)); if (verify_trust_chain(this->this, current, this->auth, FALSE, this->online)) { *cert = current; if (auth) { *auth = this->auth; } return TRUE; } } return FALSE; } METHOD(enumerator_t, trusted_destroy, void, trusted_enumerator_t *this) { DESTROY_IF(this->pretrusted); DESTROY_IF(this->auth); DESTROY_IF(this->candidates); free(this); } METHOD(credential_manager_t, create_trusted_enumerator, enumerator_t*, private_credential_manager_t *this, key_type_t type, identification_t *id, bool online) { trusted_enumerator_t *enumerator; INIT(enumerator, .public = { .enumerate = (void*)_trusted_enumerate, .destroy = _trusted_destroy, }, .this = this, .type = type, .id = id, .online = online, ); 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 config */ auth_cfg_wrapper_t *wrapper; } public_enumerator_t; METHOD(enumerator_t, public_enumerate, bool, public_enumerator_t *this, public_key_t **key, auth_cfg_t **auth) { certificate_t *cert; while (this->inner->enumerate(this->inner, &cert, auth)) { DESTROY_IF(this->current); this->current = cert->get_public_key(cert); if (this->current) { *key = this->current; return TRUE; } } return FALSE; } METHOD(enumerator_t, public_destroy, void, public_enumerator_t *this) { DESTROY_IF(this->current); this->inner->destroy(this->inner); if (this->wrapper) { remove_local_set(this->this, &this->wrapper->set); this->wrapper->destroy(this->wrapper); } this->this->lock->unlock(this->this->lock); /* check for delayed certificate cache queue */ cache_queue(this->this); free(this); } METHOD(credential_manager_t, create_public_enumerator, enumerator_t*, private_credential_manager_t *this, key_type_t type, identification_t *id, auth_cfg_t *auth) { public_enumerator_t *enumerator; INIT(enumerator, .public = { .enumerate = (void*)_public_enumerate, .destroy = _public_destroy, }, .inner = create_trusted_enumerator(this, type, id, TRUE), .this = this, ); if (auth) { enumerator->wrapper = auth_cfg_wrapper_create(auth); add_local_set(this, &enumerator->wrapper->set); } this->lock->read_lock(this->lock); return &enumerator->public; } /** * Check if an helper contains a certificate as trust anchor */ static bool auth_contains_cacert(auth_cfg_t *auth, certificate_t *cert) { enumerator_t *enumerator; identification_t *value; auth_rule_t type; bool found = FALSE; enumerator = auth->create_enumerator(auth); while (enumerator->enumerate(enumerator, &type, &value)) { if (type == AUTH_RULE_CA_CERT && cert->equals(cert, (certificate_t*)value)) { found = TRUE; break; } } enumerator->destroy(enumerator); return found; } /** * build a trustchain from subject up to a trust anchor in trusted */ static auth_cfg_t *build_trustchain(private_credential_manager_t *this, certificate_t *subject, auth_cfg_t *auth) { certificate_t *issuer, *current; auth_cfg_t *trustchain; int pathlen = 0; bool has_anchor; trustchain = auth_cfg_create(); has_anchor = auth->get(auth, AUTH_RULE_CA_CERT) != NULL; current = subject->get_ref(subject); while (TRUE) { if (auth_contains_cacert(auth, current)) { trustchain->add(trustchain, AUTH_RULE_CA_CERT, current); return trustchain; } if (subject == current) { trustchain->add(trustchain, AUTH_RULE_SUBJECT_CERT, current); } else { if (!has_anchor && this->cache->issued_by(this->cache, current, current)) { /* If no trust anchor specified, accept any CA */ trustchain->add(trustchain, AUTH_RULE_CA_CERT, current); return trustchain; } trustchain->add(trustchain, AUTH_RULE_IM_CERT, current); } if (pathlen++ > MAX_TRUST_PATH_LEN) { break; } issuer = get_issuer_cert(this, current, FALSE); if (!issuer) { if (!has_anchor) { /* If no trust anchor specified, accept incomplete chains */ return trustchain; } break; } if (has_anchor && issuer->equals(issuer, current)) { issuer->destroy(issuer); break; } current = issuer; } trustchain->destroy(trustchain); return NULL; } /** * find a private key of a give certificate */ static private_key_t *get_private_by_cert(private_credential_manager_t *this, certificate_t *cert, key_type_t type) { private_key_t *private = NULL; identification_t *keyid; chunk_t chunk; public_key_t *public; public = cert->get_public_key(cert); if (public) { if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk)) { keyid = identification_create_from_encoding(ID_KEY_ID, chunk); private = get_private_by_keyid(this, type, keyid); keyid->destroy(keyid); } public->destroy(public); } return private; } METHOD(credential_manager_t, get_private, private_key_t*, private_credential_manager_t *this, key_type_t type, identification_t *id, auth_cfg_t *auth) { enumerator_t *enumerator; certificate_t *cert; private_key_t *private = NULL; auth_cfg_t *trustchain; /* check if this is a lookup by key ID, and do it if so */ if (id && id->get_type(id) == ID_KEY_ID) { private = get_private_by_keyid(this, type, id); if (private) { return private; } } /* if a specific certificate is preferred, check for a matching key */ cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT); if (cert) { private = get_private_by_cert(this, cert, type); if (private) { trustchain = build_trustchain(this, cert, auth); if (trustchain) { auth->merge(auth, trustchain, FALSE); trustchain->destroy(trustchain); } return private; } } /* try to build a trust chain for each certificate found */ enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE); while (enumerator->enumerate(enumerator, &cert)) { private = get_private_by_cert(this, cert, type); if (private) { trustchain = build_trustchain(this, cert, auth); if (trustchain) { auth->merge(auth, trustchain, FALSE); trustchain->destroy(trustchain); 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(auth, AUTH_RULE_SUBJECT_CERT, cert->get_ref(cert)); break; } } enumerator->destroy(enumerator); } return private; } METHOD(credential_manager_t, flush_cache, void, private_credential_manager_t *this, certificate_type_t type) { this->cache->flush(this->cache, type); } METHOD(credential_manager_t, issued_by, bool, private_credential_manager_t *this, certificate_t *subject, certificate_t *issuer) { return this->cache->issued_by(this->cache, subject, issuer); } METHOD(credential_manager_t, add_set, void, private_credential_manager_t *this, credential_set_t *set) { this->lock->write_lock(this->lock); this->sets->insert_last(this->sets, set); this->lock->unlock(this->lock); } METHOD(credential_manager_t, remove_set, void, private_credential_manager_t *this, credential_set_t *set) { this->lock->write_lock(this->lock); this->sets->remove(this->sets, set, NULL); this->lock->unlock(this->lock); } METHOD(credential_manager_t, add_validator, void, private_credential_manager_t *this, cert_validator_t *vdtr) { this->lock->write_lock(this->lock); this->sets->insert_last(this->validators, vdtr); this->lock->unlock(this->lock); } METHOD(credential_manager_t, remove_validator, void, private_credential_manager_t *this, cert_validator_t *vdtr) { this->lock->write_lock(this->lock); this->validators->remove(this->validators, vdtr, NULL); this->lock->unlock(this->lock); } METHOD(credential_manager_t, destroy, void, private_credential_manager_t *this) { cache_queue(this); this->cache_queue->destroy(this->cache_queue); this->sets->remove(this->sets, this->cache, NULL); this->sets->destroy(this->sets); this->local_sets->destroy(this->local_sets); this->cache->destroy(this->cache); this->validators->destroy(this->validators); this->lock->destroy(this->lock); this->queue_mutex->destroy(this->queue_mutex); free(this); } /* * see header file */ credential_manager_t *credential_manager_create() { private_credential_manager_t *this; INIT(this, .public = { .create_cert_enumerator = _create_cert_enumerator, .create_shared_enumerator = _create_shared_enumerator, .create_cdp_enumerator = _create_cdp_enumerator, .get_cert = _get_cert, .get_shared = _get_shared, .get_private = _get_private, .create_trusted_enumerator = _create_trusted_enumerator, .create_public_enumerator = _create_public_enumerator, .flush_cache = _flush_cache, .cache_cert = _cache_cert, .issued_by = _issued_by, .add_set = _add_set, .remove_set = _remove_set, .add_local_set = _add_local_set, .remove_local_set = _remove_local_set, .add_validator = _add_validator, .remove_validator = _remove_validator, .destroy = _destroy, }, .sets = linked_list_create(), .validators = linked_list_create(), .cache = cert_cache_create(), .cache_queue = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .queue_mutex = mutex_create(MUTEX_TYPE_DEFAULT), ); this->local_sets = thread_value_create((thread_cleanup_t)this->sets->destroy); this->sets->insert_first(this->sets, this->cache); return &this->public; }