child-rekey: Don't change state to INSTALLED if it was already REKEYING
[strongswan.git] / src / libstrongswan / credentials / credential_manager.c
index 8704cd2..95c5cd7 100644 (file)
@@ -1,4 +1,5 @@
 /*
+ * Copyright (C) 2015 Tobias Brunner
  * Copyright (C) 2007 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
 #include "credential_manager.h"
 
 #include <library.h>
-#include <debug.h>
+#include <utils/debug.h>
 #include <threading/thread_value.h>
 #include <threading/mutex.h>
 #include <threading/rwlock.h>
-#include <utils/linked_list.h>
+#include <collections/linked_list.h>
 #include <credentials/sets/cert_cache.h>
 #include <credentials/sets/auth_cfg_wrapper.h>
 #include <credentials/certificates/x509.h>
@@ -81,6 +82,16 @@ struct private_credential_manager_t {
         * mutex for cache queue
         */
        mutex_t *queue_mutex;
+
+       /**
+        * Registered hook to call on validation errors
+        */
+       credential_hook_t hook;
+
+       /**
+        * Registered data to pass to hook
+        */
+       void *hook_data;
 };
 
 /** data to pass to create_private_enumerator */
@@ -126,6 +137,22 @@ typedef struct {
        enumerator_t *exclusive;
 } sets_enumerator_t;
 
+METHOD(credential_manager_t, set_hook, void,
+       private_credential_manager_t *this, credential_hook_t hook, void *data)
+{
+       this->hook = hook;
+       this->hook_data = data;
+}
+
+METHOD(credential_manager_t, call_hook, void,
+       private_credential_manager_t *this, credential_hook_type_t type,
+       certificate_t *cert)
+{
+       if (this->hook)
+       {
+               this->hook(this->hook_data, type, cert);
+       }
+}
 
 METHOD(enumerator_t, sets_enumerate, bool,
        sets_enumerator_t *this, credential_set_t **set)
@@ -378,8 +405,8 @@ METHOD(credential_manager_t, get_shared, shared_key_t*,
        identification_t *me, identification_t *other)
 {
        shared_key_t *current, *found = NULL;
-       id_match_t *best_me = ID_MATCH_NONE, *best_other = ID_MATCH_NONE;
-       id_match_t *match_me, *match_other;
+       id_match_t best_me = ID_MATCH_NONE, best_other = ID_MATCH_NONE;
+       id_match_t match_me, match_other;
        enumerator_t *enumerator;
 
        enumerator = create_shared_enumerator(this, type, me, other);
@@ -393,6 +420,10 @@ METHOD(credential_manager_t, get_shared, shared_key_t*,
                        best_me = match_me;
                        best_other = match_other;
                }
+               if (best_me == ID_MATCH_PERFECT && best_other == ID_MATCH_PERFECT)
+               {
+                       break;
+               }
        }
        enumerator->destroy(enumerator);
        return found;
@@ -450,6 +481,17 @@ METHOD(credential_manager_t, remove_local_set, void,
        }
 }
 
+METHOD(credential_manager_t, issued_by, bool,
+       private_credential_manager_t *this, certificate_t *subject,
+       certificate_t *issuer, signature_scheme_t *scheme)
+{
+       if (this->cache)
+       {
+               return this->cache->issued_by(this->cache, subject, issuer, scheme);
+       }
+       return subject->issued_by(subject, issuer, scheme);
+}
+
 METHOD(credential_manager_t, cache_cert, void,
        private_credential_manager_t *this, certificate_t *cert)
 {
@@ -504,32 +546,76 @@ static void cache_queue(private_credential_manager_t *this)
 }
 
 /**
+ * Use validators to check the lifetime of certificates
+ */
+static bool check_lifetime(private_credential_manager_t *this,
+                                                  certificate_t *cert, char *label,
+                                                  int pathlen, bool trusted, auth_cfg_t *auth)
+{
+       time_t not_before, not_after;
+       cert_validator_t *validator;
+       enumerator_t *enumerator;
+       status_t status = NEED_MORE;
+
+       enumerator = this->validators->create_enumerator(this->validators);
+       while (enumerator->enumerate(enumerator, &validator))
+       {
+               if (!validator->check_lifetime)
+               {
+                       continue;
+               }
+               status = validator->check_lifetime(validator, cert,
+                                                                                  pathlen, trusted, auth);
+               if (status != NEED_MORE)
+               {
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       switch (status)
+       {
+               case NEED_MORE:
+                       if (!cert->get_validity(cert, NULL, &not_before, &not_after))
+                       {
+                               DBG1(DBG_CFG, "%s certificate invalid (valid from %T to %T)",
+                                        label, &not_before, FALSE, &not_after, FALSE);
+                               break;
+                       }
+                       return TRUE;
+               case SUCCESS:
+                       return TRUE;
+               case FAILED:
+               default:
+                       break;
+       }
+       call_hook(this, CRED_HOOK_EXPIRED, cert);
+       return FALSE;
+}
+
+/**
  * check a certificate for its lifetime
  */
 static bool check_certificate(private_credential_manager_t *this,
                                certificate_t *subject, certificate_t *issuer, bool online,
                                int pathlen, bool trusted, auth_cfg_t *auth)
 {
-       time_t not_before, not_after;
        cert_validator_t *validator;
        enumerator_t *enumerator;
 
-       if (!subject->get_validity(subject, NULL, &not_before, &not_after))
+       if (!check_lifetime(this, subject, "subject", pathlen, FALSE, auth) ||
+               !check_lifetime(this, issuer, "issuer", pathlen + 1, trusted, auth))
        {
-               DBG1(DBG_CFG, "subject certificate invalid (valid from %T to %T)",
-                        &not_before, FALSE, &not_after, FALSE);
-               return FALSE;
-       }
-       if (!issuer->get_validity(issuer, NULL, &not_before, &not_after))
-       {
-               DBG1(DBG_CFG, "issuer certificate invalid (valid from %T to %T)",
-                        &not_before, FALSE, &not_after, FALSE);
                return FALSE;
        }
 
        enumerator = this->validators->create_enumerator(this->validators);
        while (enumerator->enumerate(enumerator, &validator))
        {
+               if (!validator->validate)
+               {
+                       continue;
+               }
                if (!validator->validate(validator, subject, issuer,
                                                                 online, pathlen, trusted, auth))
                {
@@ -579,7 +665,7 @@ static certificate_t *get_issuer_cert(private_credential_manager_t *this,
                                                                                subject->get_issuer(subject), trusted);
        while (enumerator->enumerate(enumerator, &candidate))
        {
-               if (this->cache->issued_by(this->cache, subject, candidate, scheme))
+               if (issued_by(this, subject, candidate, scheme))
                {
                        issuer = candidate->get_ref(candidate);
                        break;
@@ -613,6 +699,9 @@ static void get_key_strength(certificate_t *cert, auth_cfg_t *auth)
                        case KEY_ECDSA:
                                auth->add(auth, AUTH_RULE_ECDSA_STRENGTH, strength);
                                break;
+                       case KEY_BLISS:
+                               auth->add(auth, AUTH_RULE_BLISS_STRENGTH, strength);
+                               break;
                        default:
                                break;
                }
@@ -643,7 +732,7 @@ static bool verify_trust_chain(private_credential_manager_t *this,
                if (issuer)
                {
                        /* accept only self-signed CAs as trust anchor */
-                       if (this->cache->issued_by(this->cache, issuer, issuer, NULL))
+                       if (issued_by(this, issuer, issuer, NULL))
                        {
                                auth->add(auth, AUTH_RULE_CA_CERT, issuer->get_ref(issuer));
                                DBG1(DBG_CFG, "  using trusted ca certificate \"%Y\"",
@@ -665,9 +754,10 @@ static bool verify_trust_chain(private_credential_manager_t *this,
                        {
                                if (current->equals(current, issuer))
                                {
-                                       DBG1(DBG_CFG, "  self-signed certificate \"%Y\" is not trusted",
-                                                current->get_subject(current));
+                                       DBG1(DBG_CFG, "  self-signed certificate \"%Y\" is not "
+                                                "trusted", current->get_subject(current));
                                        issuer->destroy(issuer);
+                                       call_hook(this, CRED_HOOK_UNTRUSTED_ROOT, current);
                                        break;
                                }
                                auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer));
@@ -679,6 +769,7 @@ static bool verify_trust_chain(private_credential_manager_t *this,
                        {
                                DBG1(DBG_CFG, "no issuer certificate found for \"%Y\"",
                                         current->get_subject(current));
+                               call_hook(this, CRED_HOOK_NO_ISSUER, current);
                                break;
                        }
                }
@@ -697,8 +788,8 @@ static bool verify_trust_chain(private_credential_manager_t *this,
                current = issuer;
                if (trusted)
                {
-                       DBG1(DBG_CFG, "  reached self-signed root ca with a path length of %d",
-                                                 pathlen);
+                       DBG1(DBG_CFG, "  reached self-signed root ca with a "
+                                "path length of %d", pathlen);
                        break;
                }
        }
@@ -706,6 +797,7 @@ static bool verify_trust_chain(private_credential_manager_t *this,
        if (pathlen > MAX_TRUST_PATH_LEN)
        {
                DBG1(DBG_CFG, "maximum path length of %d exceeded", MAX_TRUST_PATH_LEN);
+               call_hook(this, CRED_HOOK_EXCEEDED_PATH_LEN, subject);
        }
        if (trusted)
        {
@@ -767,8 +859,7 @@ METHOD(enumerator_t, trusted_enumerate, bool,
                        /* if we find a trusted self signed certificate, we just accept it.
                         * However, in order to fulfill authorization rules, we try to build
                         * the trust chain if it is not self signed */
-                       if (this->this->cache->issued_by(this->this->cache,
-                                                                  this->pretrusted, this->pretrusted, NULL) ||
+                       if (issued_by(this->this, this->pretrusted, this->pretrusted, NULL) ||
                                verify_trust_chain(this->this, this->pretrusted, this->auth,
                                                                   TRUE, this->online))
                        {
@@ -827,6 +918,8 @@ METHOD(enumerator_t, trusted_destroy, void,
        DESTROY_IF(this->auth);
        DESTROY_IF(this->candidates);
        this->failed->destroy_offset(this->failed, offsetof(certificate_t, destroy));
+       /* check for delayed certificate cache queue */
+       cache_queue(this->this);
        free(this);
 }
 
@@ -895,7 +988,6 @@ METHOD(enumerator_t, public_destroy, void,
                this->wrapper->destroy(this->wrapper);
        }
        this->this->lock->unlock(this->this->lock);
-
        /* check for delayed certificate cache queue */
        cache_queue(this->this);
        free(this);
@@ -903,7 +995,7 @@ METHOD(enumerator_t, public_destroy, void,
 
 METHOD(credential_manager_t, create_public_enumerator, enumerator_t*,
        private_credential_manager_t *this, key_type_t type, identification_t *id,
-       auth_cfg_t *auth)
+       auth_cfg_t *auth, bool online)
 {
        public_enumerator_t *enumerator;
 
@@ -912,7 +1004,7 @@ METHOD(credential_manager_t, create_public_enumerator, enumerator_t*,
                        .enumerate = (void*)_public_enumerate,
                        .destroy = _public_destroy,
                },
-               .inner = create_trusted_enumerator(this, type, id, TRUE),
+               .inner = create_trusted_enumerator(this, type, id, online),
                .this = this,
        );
        if (auth)
@@ -975,8 +1067,7 @@ static auth_cfg_t *build_trustchain(private_credential_manager_t *this,
                }
                else
                {
-                       if (!has_anchor &&
-                               this->cache->issued_by(this->cache, current, current, NULL))
+                       if (!has_anchor && issued_by(this, current, current, NULL))
                        {       /* If no trust anchor specified, accept any CA */
                                trustchain->add(trustchain, AUTH_RULE_CA_CERT, current);
                                return trustchain;
@@ -1032,6 +1123,29 @@ static private_key_t *get_private_by_cert(private_credential_manager_t *this,
        return private;
 }
 
+/**
+ * Move the actually used certificate to front, so it gets returned with get()
+ */
+static void prefer_cert(auth_cfg_t *auth, certificate_t *cert)
+{
+       enumerator_t *enumerator;
+       auth_rule_t rule;
+       certificate_t *current;
+
+       enumerator = auth->create_enumerator(auth);
+       while (enumerator->enumerate(enumerator, &rule, &current))
+       {
+               if (rule == AUTH_RULE_SUBJECT_CERT)
+               {
+                       current->get_ref(current);
+                       auth->replace(auth, enumerator, AUTH_RULE_SUBJECT_CERT, cert);
+                       cert = current;
+               }
+       }
+       enumerator->destroy(enumerator);
+       auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert);
+}
+
 METHOD(credential_manager_t, get_private, private_key_t*,
        private_credential_manager_t *this, key_type_t type, identification_t *id,
        auth_cfg_t *auth)
@@ -1040,6 +1154,7 @@ METHOD(credential_manager_t, get_private, private_key_t*,
        certificate_t *cert;
        private_key_t *private = NULL;
        auth_cfg_t *trustchain;
+       auth_rule_t rule;
 
        /* check if this is a lookup by key ID, and do it if so */
        if (id && id->get_type(id) == ID_KEY_ID)
@@ -1053,7 +1168,35 @@ METHOD(credential_manager_t, get_private, private_key_t*,
 
        if (auth)
        {
-               /* if a specific certificate is preferred, check for a matching key */
+               /* try to find a trustchain with one of the configured subject certs */
+               enumerator = auth->create_enumerator(auth);
+               while (enumerator->enumerate(enumerator, &rule, &cert))
+               {
+                       if (rule == AUTH_RULE_SUBJECT_CERT)
+                       {
+                               private = get_private_by_cert(this, cert, type);
+                               if (private)
+                               {
+                                       trustchain = build_trustchain(this, cert, auth);
+                                       if (trustchain)
+                                       {
+                                               auth->merge(auth, trustchain, FALSE);
+                                               prefer_cert(auth, cert->get_ref(cert));
+                                               trustchain->destroy(trustchain);
+                                               break;
+                                       }
+                                       private->destroy(private);
+                                       private = NULL;
+                               }
+                       }
+               }
+               enumerator->destroy(enumerator);
+               if (private)
+               {
+                       return private;
+               }
+
+               /* if none yielded a trustchain, enforce the first configured cert */
                cert = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
                if (cert)
                {
@@ -1115,14 +1258,10 @@ METHOD(credential_manager_t, get_private, private_key_t*,
 METHOD(credential_manager_t, flush_cache, void,
        private_credential_manager_t *this, certificate_type_t type)
 {
-       this->cache->flush(this->cache, type);
-}
-
-METHOD(credential_manager_t, issued_by, bool,
-       private_credential_manager_t *this, certificate_t *subject,
-       certificate_t *issuer, signature_scheme_t *scheme)
-{
-       return this->cache->issued_by(this->cache, subject, issuer, scheme);
+       if (this->cache)
+       {
+               this->cache->flush(this->cache, type);
+       }
 }
 
 METHOD(credential_manager_t, add_set, void,
@@ -1145,7 +1284,7 @@ METHOD(credential_manager_t, add_validator, void,
        private_credential_manager_t *this, cert_validator_t *vdtr)
 {
        this->lock->write_lock(this->lock);
-       this->sets->insert_last(this->validators, vdtr);
+       this->validators->insert_last(this->validators, vdtr);
        this->lock->unlock(this->lock);
 }
 
@@ -1162,11 +1301,14 @@ METHOD(credential_manager_t, destroy, void,
 {
        cache_queue(this);
        this->cache_queue->destroy(this->cache_queue);
-       this->sets->remove(this->sets, this->cache, NULL);
+       if (this->cache)
+       {
+               this->sets->remove(this->sets, this->cache, NULL);
+               this->cache->destroy(this->cache);
+       }
        this->sets->destroy(this->sets);
        this->local_sets->destroy(this->local_sets);
        this->exclusive_local_sets->destroy(this->exclusive_local_sets);
-       this->cache->destroy(this->cache);
        this->validators->destroy(this->validators);
        this->lock->destroy(this->lock);
        this->queue_mutex->destroy(this->queue_mutex);
@@ -1199,11 +1341,12 @@ credential_manager_t *credential_manager_create()
                        .remove_local_set = _remove_local_set,
                        .add_validator = _add_validator,
                        .remove_validator = _remove_validator,
+                       .set_hook = _set_hook,
+                       .call_hook = _call_hook,
                        .destroy = _destroy,
                },
                .sets = linked_list_create(),
                .validators = linked_list_create(),
-               .cache = cert_cache_create(),
                .cache_queue = linked_list_create(),
                .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
                .queue_mutex = mutex_create(MUTEX_TYPE_DEFAULT),
@@ -1211,7 +1354,11 @@ credential_manager_t *credential_manager_create()
 
        this->local_sets = thread_value_create((thread_cleanup_t)this->sets->destroy);
        this->exclusive_local_sets = thread_value_create((thread_cleanup_t)this->sets->destroy);
-       this->sets->insert_first(this->sets, this->cache);
+       if (lib->settings->get_bool(lib->settings, "%s.cert_cache", TRUE, lib->ns))
+       {
+               this->cache = cert_cache_create();
+               this->sets->insert_first(this->sets, this->cache);
+       }
 
        return &this->public;
 }