credmgr: introduce a hook function to catch trust chain validation errors
authorMartin Willi <martin@revosec.ch>
Tue, 9 Jul 2013 09:55:32 +0000 (11:55 +0200)
committerMartin Willi <martin@revosec.ch>
Thu, 18 Jul 2013 14:00:30 +0000 (16:00 +0200)
src/libcharon/plugins/addrblock/addrblock_validator.c
src/libcharon/plugins/coupling/coupling_validator.c
src/libstrongswan/credentials/cert_validator.h
src/libstrongswan/credentials/credential_manager.c
src/libstrongswan/credentials/credential_manager.h
src/libstrongswan/plugins/constraints/constraints_validator.c
src/libstrongswan/plugins/revocation/revocation_validator.c

index 65f4ed0..372c978 100644 (file)
@@ -94,7 +94,12 @@ METHOD(cert_validator_t, validate, bool,
        if (subject->get_type(subject) == CERT_X509 &&
                issuer->get_type(issuer) == CERT_X509)
        {
-               return check_addrblock((x509_t*)subject, (x509_t*)issuer);
+               if (!check_addrblock((x509_t*)subject, (x509_t*)issuer))
+               {
+                       lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION,
+                                                                       subject);
+                       return FALSE;
+               }
        }
        return TRUE;
 }
index 539be75..5a72531 100644 (file)
@@ -167,6 +167,8 @@ METHOD(cert_validator_t, validate, bool,
                        {
                                DBG1(DBG_CFG, "coupling new certificate '%Y' failed",
                                         subject->get_subject(subject));
+                               lib->credmgr->call_hook(lib->credmgr
+                                                                               CRED_HOOK_POLICY_VIOLATION, subject);
                        }
                }
                else
@@ -174,6 +176,8 @@ METHOD(cert_validator_t, validate, bool,
                        DBG1(DBG_CFG, "coupling new certificate '%Y' failed, limit of %d "
                                 "couplings reached", subject->get_subject(subject),
                                 this->max_couplings);
+                       lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION,
+                                                                       subject);
                }
                this->mutex->unlock(this->mutex);
        }
index 325fa0a..6b28f35 100644 (file)
@@ -53,6 +53,9 @@ struct cert_validator_t {
        /**
         * Validate a subject certificate in relation to its issuer.
         *
+        * If FALSE is returned, the validator should call_hook() on the
+        * credential manager with an appropriate type and the certificate.
+        *
         * @param subject               subject certificate to check
         * @param issuer                issuer of subject
         * @param online                whether to do online revocation checking
index fa25555..de19c8d 100644 (file)
@@ -81,6 +81,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 +136,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)
@@ -553,15 +579,17 @@ static bool check_lifetime(private_credential_manager_t *this,
                        {
                                DBG1(DBG_CFG, "%s certificate invalid (valid from %T to %T)",
                                         label, &not_before, FALSE, &not_after, FALSE);
-                               return FALSE;
+                               break;
                        }
                        return TRUE;
                case SUCCESS:
                        return TRUE;
                case FAILED:
                default:
-                       return FALSE;
+                       break;
        }
+       call_hook(this, CRED_HOOK_EXPIRED, cert);
+       return FALSE;
 }
 
 /**
@@ -722,9 +750,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));
@@ -736,6 +765,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;
                        }
                }
@@ -754,8 +784,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;
                }
        }
@@ -763,6 +793,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)
        {
@@ -1305,6 +1336,8 @@ 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(),
index 73c5857..445ea3f 100644 (file)
@@ -22,6 +22,7 @@
 #define CREDENTIAL_MANAGER_H_
 
 typedef struct credential_manager_t credential_manager_t;
+typedef enum credential_hook_type_t credential_hook_type_t;
 
 #include <utils/identification.h>
 #include <collections/enumerator.h>
@@ -33,6 +34,37 @@ typedef struct credential_manager_t credential_manager_t;
 #include <credentials/cert_validator.h>
 
 /**
+ * Type of a credential hook error/event.
+ */
+enum credential_hook_type_t {
+       /** The certificate has expired (or is not yet valid) */
+       CRED_HOOK_EXPIRED,
+       /** The certificate has been revoked */
+       CRED_HOOK_REVOKED,
+       /** Checking certificate revocation failed. This does not necessarily mean
+        *  the certificate is rejected, just that revocation checking failed. */
+       CRED_HOOK_VALIDATION_FAILED,
+       /** No trusted issuer certificate has been found for this certificate */
+       CRED_HOOK_NO_ISSUER,
+       /** Encountered a self-signed (root) certificate, but it is not trusted */
+       CRED_HOOK_UNTRUSTED_ROOT,
+       /** Maximum trust chain length exceeded for certificate */
+       CRED_HOOK_EXCEEDED_PATH_LEN,
+       /** The certificate violates some other kind of policy and gets rejected */
+       CRED_HOOK_POLICY_VIOLATION,
+};
+
+/**
+ * Hook function to invoke on certificate validation errors.
+ *
+ * @param data                 user data supplied during hook registration
+ * @param type                 type of validation error/event
+ * @param cert                 associated certificate
+ */
+typedef void (*credential_hook_t)(void *data, credential_hook_type_t type,
+                                                                 certificate_t *cert);
+
+/**
  * Manages credentials using credential_sets.
  *
  * The credential manager is the entry point of the credential framework. It
@@ -263,6 +295,28 @@ struct credential_manager_t {
        void (*remove_validator)(credential_manager_t *this, cert_validator_t *vdtr);
 
        /**
+        * Set a hook to call on certain credential validation errors.
+        *
+        * @param hook          hook to register, NULL to unregister
+        * @param data          data to pass to hook
+        */
+       void (*set_hook)(credential_manager_t *this, credential_hook_t hook,
+                                        void *data);
+
+       /**
+        * Call the registered credential hook, if any.
+        *
+        * While hooks are usually called by the credential manager itself, some
+        * validator plugins might raise hooks as well if they consider certificates
+        * invalid.
+        *
+        * @param type          type of the event
+        * @param cert          associated certificate
+        */
+       void (*call_hook)(credential_manager_t *this, credential_hook_type_t type,
+                                         certificate_t *cert);
+
+       /**
         * Destroy a credential_manager instance.
         */
        void (*destroy)(credential_manager_t *this);
index 83a7429..62ccc71 100644 (file)
@@ -533,20 +533,28 @@ METHOD(cert_validator_t, validate, bool,
        {
                if (!check_pathlen((x509_t*)issuer, pathlen))
                {
+                       lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_EXCEEDED_PATH_LEN,
+                                                                       subject);
                        return FALSE;
                }
                if (!check_name_constraints(subject, (x509_t*)issuer))
                {
+                       lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION,
+                                                                       subject);
                        return FALSE;
                }
                if (!check_policy((x509_t*)subject, (x509_t*)issuer, !pathlen, auth))
                {
+                       lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION,
+                                                                       subject);
                        return FALSE;
                }
                if (anchor)
                {
                        if (!check_policy_constraints((x509_t*)issuer, pathlen, auth))
                        {
+                               lib->credmgr->call_hook(lib->credmgr,
+                                                                               CRED_HOOK_POLICY_VIOLATION, issuer);
                                return FALSE;
                        }
                }
index 44c2345..c8ec3f7 100644 (file)
@@ -691,6 +691,8 @@ METHOD(cert_validator_t, validate, bool,
                        case VALIDATION_REVOKED:
                        case VALIDATION_ON_HOLD:
                                /* has already been logged */
+                               lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_REVOKED,
+                                                                               subject);
                                return FALSE;
                        case VALIDATION_SKIPPED:
                                DBG2(DBG_CFG, "ocsp check skipped, no ocsp found");
@@ -711,6 +713,8 @@ METHOD(cert_validator_t, validate, bool,
                        case VALIDATION_REVOKED:
                        case VALIDATION_ON_HOLD:
                                /* has already been logged */
+                               lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_REVOKED,
+                                                                               subject);
                                return FALSE;
                        case VALIDATION_FAILED:
                        case VALIDATION_SKIPPED:
@@ -720,6 +724,8 @@ METHOD(cert_validator_t, validate, bool,
                                DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
                                break;
                }
+               lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_VALIDATION_FAILED,
+                                                               subject);
        }
        return TRUE;
 }