revocation: Set defaults if CRL/OCSP checking is disabled in config
[strongswan.git] / src / libstrongswan / plugins / revocation / revocation_validator.c
index ff3ef14..edb2f80 100644 (file)
@@ -1,8 +1,9 @@
 /*
+ * Copyright (C) 2015-2018 Tobias Brunner
  * Copyright (C) 2010 Martin Willi
  * Copyright (C) 2010 revosec AG
  * Copyright (C) 2009 Andreas Steffen
- * Hochschule fuer Technik Rapperswil
+ * HSR 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
  * for more details.
  */
 
+#include <time.h>
+
 #include "revocation_validator.h"
 
-#include <debug.h>
+#include <utils/debug.h>
 #include <credentials/certificates/x509.h>
 #include <credentials/certificates/crl.h>
 #include <credentials/certificates/ocsp_request.h>
@@ -36,6 +39,17 @@ struct private_revocation_validator_t {
         * Public revocation_validator_t interface.
         */
        revocation_validator_t public;
+
+       /**
+        * Enable OCSP validation
+        */
+       bool enable_ocsp;
+
+       /**
+        * Enable CRL validation
+        */
+       bool enable_crl;
+
 };
 
 /**
@@ -93,40 +107,92 @@ static certificate_t *fetch_ocsp(char *url, certificate_t *subject,
 /**
  * check the signature of an OCSP response
  */
-static bool verify_ocsp(ocsp_response_t *response, auth_cfg_t *auth)
+static bool verify_ocsp(ocsp_response_t *response, certificate_t *ca)
 {
        certificate_t *issuer, *subject;
        identification_t *responder;
        ocsp_response_wrapper_t *wrapper;
        enumerator_t *enumerator;
-       auth_cfg_t *current;
-       bool verified = FALSE;
+       x509_t *x509;
+       bool verified = FALSE, found = FALSE;
 
        wrapper = ocsp_response_wrapper_create((ocsp_response_t*)response);
        lib->credmgr->add_local_set(lib->credmgr, &wrapper->set, FALSE);
 
        subject = &response->certificate;
        responder = subject->get_issuer(subject);
-       enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr,
+
+       /* check OCSP response using CA or directly delegated OCSP signer */
+       enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, CERT_X509,
                                                                                                        KEY_ANY, responder, FALSE);
-       while (enumerator->enumerate(enumerator, &issuer, &current))
+       while (enumerator->enumerate(enumerator, &issuer))
        {
-               if (lib->credmgr->issued_by(lib->credmgr, subject, issuer))
+               x509 = (x509_t*)issuer;
+               if (!issuer->get_validity(issuer, NULL, NULL, NULL))
+               {       /* OCSP signer currently invalid */
+                       continue;
+               }
+               if (!ca->equals(ca, issuer))
+               {       /* delegated OCSP signer? */
+                       if (!lib->credmgr->issued_by(lib->credmgr, issuer, ca, NULL))
+                       {       /* OCSP response not signed by CA, nor delegated OCSP signer */
+                               continue;
+                       }
+                       if (!(x509->get_flags(x509) & X509_OCSP_SIGNER))
+                       {       /* delegated OCSP signer does not have OCSP signer flag */
+                               continue;
+                       }
+               }
+               found = TRUE;
+               if (lib->credmgr->issued_by(lib->credmgr, subject, issuer, NULL))
                {
                        DBG1(DBG_CFG, "  ocsp response correctly signed by \"%Y\"",
-                                                        issuer->get_subject(issuer));
-                       if (auth)
-                       {
-                               auth->merge(auth, current, FALSE);
-                       }
+                                issuer->get_subject(issuer));
                        verified = TRUE;
                        break;
                }
+               DBG1(DBG_CFG, "ocsp response verification failed, "
+                        "invalid signature");
        }
        enumerator->destroy(enumerator);
 
+       if (!verified)
+       {
+               /* as fallback, use any locally installed OCSP signer certificate */
+               enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
+                                                                               CERT_X509, KEY_ANY, responder, TRUE);
+               while (enumerator->enumerate(enumerator, &issuer))
+               {
+                       x509 = (x509_t*)issuer;
+                       /* while issued_by() accepts both OCSP signer or CA basic
+                        * constraint flags to verify OCSP responses, unrelated but trusted
+                        * OCSP signers must explicitly have the OCSP signer flag set. */
+                       if ((x509->get_flags(x509) & X509_OCSP_SIGNER) &&
+                               issuer->get_validity(issuer, NULL, NULL, NULL))
+                       {
+                               found = TRUE;
+                               if (lib->credmgr->issued_by(lib->credmgr, subject, issuer, NULL))
+                               {
+                                       DBG1(DBG_CFG, "  ocsp response correctly signed by \"%Y\"",
+                                                issuer->get_subject(issuer));
+                                       verified = TRUE;
+                                       break;
+                               }
+                               DBG1(DBG_CFG, "ocsp response verification failed, "
+                                        "invalid signature");
+                       }
+               }
+               enumerator->destroy(enumerator);
+       }
+
        lib->credmgr->remove_local_set(lib->credmgr, &wrapper->set);
        wrapper->destroy(wrapper);
+
+       if (!found)
+       {
+               DBG1(DBG_CFG, "ocsp response verification failed, "
+                        "no signer certificate '%Y' found", responder);
+       }
        return verified;
 }
 
@@ -134,8 +200,8 @@ static bool verify_ocsp(ocsp_response_t *response, auth_cfg_t *auth)
  * Get the better of two OCSP responses, and check for usable OCSP info
  */
 static certificate_t *get_better_ocsp(certificate_t *cand, certificate_t *best,
-                                       x509_t *subject, x509_t *issuer, cert_validation_t *valid,
-                                       auth_cfg_t *auth, bool cache)
+                                                                         x509_t *subject, x509_t *issuer,
+                                                                         cert_validation_t *valid, bool cache)
 {
        ocsp_response_t *response;
        time_t revocation, this_update, next_update, valid_until;
@@ -145,9 +211,8 @@ static certificate_t *get_better_ocsp(certificate_t *cand, certificate_t *best,
        response = (ocsp_response_t*)cand;
 
        /* check ocsp signature */
-       if (!verify_ocsp(response, auth))
+       if (!verify_ocsp(response, &issuer->interface))
        {
-               DBG1(DBG_CFG, "ocsp response verification failed");
                cand->destroy(cand);
                return best;
        }
@@ -226,8 +291,7 @@ static cert_validation_t check_ocsp(x509_t *subject, x509_t *issuer,
        while (enumerator->enumerate(enumerator, &current))
        {
                current->get_ref(current);
-               best = get_better_ocsp(current, best, subject, issuer,
-                                                          &valid, auth, FALSE);
+               best = get_better_ocsp(current, best, subject, issuer, &valid, FALSE);
                if (best && valid != VALIDATION_STALE)
                {
                        DBG1(DBG_CFG, "  using cached ocsp response");
@@ -254,7 +318,7 @@ static cert_validation_t check_ocsp(x509_t *subject, x509_t *issuer,
                        if (current)
                        {
                                best = get_better_ocsp(current, best, subject, issuer,
-                                                                          &valid, auth, TRUE);
+                                                                          &valid, TRUE);
                                if (best && valid != VALIDATION_STALE)
                                {
                                        break;
@@ -276,7 +340,7 @@ static cert_validation_t check_ocsp(x509_t *subject, x509_t *issuer,
                        if (current)
                        {
                                best = get_better_ocsp(current, best, subject, issuer,
-                                                                          &valid, auth, TRUE);
+                                                                          &valid, TRUE);
                                if (best && valid != VALIDATION_STALE)
                                {
                                        break;
@@ -290,13 +354,10 @@ static cert_validation_t check_ocsp(x509_t *subject, x509_t *issuer,
        {
                valid = VALIDATION_FAILED;
        }
-       if (auth)
-       {
-               auth->add(auth, AUTH_RULE_OCSP_VALIDATION, valid);
-               if (valid == VALIDATION_GOOD)
-               {       /* successful OCSP check fulfills also CRL constraint */
-                       auth->add(auth, AUTH_RULE_CRL_VALIDATION, VALIDATION_GOOD);
-               }
+       auth->add(auth, AUTH_RULE_OCSP_VALIDATION, valid);
+       if (valid == VALIDATION_GOOD)
+       {       /* successful OCSP check fulfills also CRL constraint */
+               auth->add(auth, AUTH_RULE_CRL_VALIDATION, VALIDATION_GOOD);
        }
        DESTROY_IF(best);
        return valid;
@@ -317,7 +378,7 @@ static certificate_t* fetch_crl(char *url)
                return NULL;
        }
        crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
-                                                        BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
+                                                        BUILD_BLOB_PEM, chunk, BUILD_END);
        chunk_free(&chunk);
        if (!crl)
        {
@@ -330,25 +391,20 @@ static certificate_t* fetch_crl(char *url)
 /**
  * check the signature of an CRL
  */
-static bool verify_crl(certificate_t *crl, auth_cfg_t *auth)
+static bool verify_crl(certificate_t *crl)
 {
        certificate_t *issuer;
        enumerator_t *enumerator;
        bool verified = FALSE;
-       auth_cfg_t *current;
 
        enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr,
                                                                                KEY_ANY, crl->get_issuer(crl), FALSE);
-       while (enumerator->enumerate(enumerator, &issuer, &current))
+       while (enumerator->enumerate(enumerator, &issuer, NULL))
        {
-               if (lib->credmgr->issued_by(lib->credmgr, crl, issuer))
+               if (lib->credmgr->issued_by(lib->credmgr, crl, issuer, NULL))
                {
                        DBG1(DBG_CFG, "  crl correctly signed by \"%Y\"",
                                                   issuer->get_subject(issuer));
-                       if (auth)
-                       {
-                               auth->merge(auth, current, FALSE);
-                       }
                        verified = TRUE;
                        break;
                }
@@ -359,16 +415,55 @@ static bool verify_crl(certificate_t *crl, auth_cfg_t *auth)
 }
 
 /**
+ * Report the given CRL's validity and cache it if valid and requested
+ */
+static bool is_crl_valid(certificate_t *crl, time_t now, bool cache)
+{
+       time_t valid_until;
+
+       if (crl->get_validity(crl, &now, NULL, &valid_until))
+       {
+               DBG1(DBG_CFG, "  crl is valid: until %T", &valid_until, FALSE);
+               if (cache)
+               {
+                       lib->credmgr->cache_cert(lib->credmgr, crl);
+               }
+               return TRUE;
+       }
+       DBG1(DBG_CFG, "  crl is stale: since %T", &valid_until, FALSE);
+       return FALSE;
+}
+
+/**
+ * Check if the CRL should be used yet
+ */
+static bool is_crl_not_valid_yet(certificate_t *crl, time_t now)
+{
+       time_t this_update;
+
+       if (!crl->get_validity(crl, &now, &this_update, NULL))
+       {
+               if (this_update > now)
+               {
+                       DBG1(DBG_CFG, "  crl is not valid: until %T", &this_update, FALSE);
+                       return TRUE;
+               }
+               /* we accept stale CRLs */
+       }
+       return FALSE;
+}
+
+/**
  * Get the better of two CRLs, and check for usable CRL info
  */
 static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
-                                       x509_t *subject, cert_validation_t *valid, auth_cfg_t *auth,
+                                       x509_t *subject, cert_validation_t *valid,
                                        bool cache, crl_t *base)
 {
        enumerator_t *enumerator;
-       time_t revocation, valid_until;
+       time_t now, revocation;
        crl_reason_t reason;
-       chunk_t serial;
+       chunk_t subject_serial, serial;
        crl_t *crl = (crl_t*)cand;
 
        if (base)
@@ -390,20 +485,25 @@ static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
        }
 
        /* check CRL signature */
-       if (!verify_crl(cand, auth))
+       if (!verify_crl(cand))
        {
                DBG1(DBG_CFG, "crl response verification failed");
                cand->destroy(cand);
                return best;
        }
+       now = time(NULL);
+       if (is_crl_not_valid_yet(cand, now))
+       {
+               cand->destroy(cand);
+               return best;
+       }
 
+       subject_serial = chunk_skip_zero(subject->get_serial(subject));
        enumerator = crl->create_enumerator(crl);
        while (enumerator->enumerate(enumerator, &serial, &revocation, &reason))
        {
-               if (chunk_equals(serial, subject->get_serial(subject)))
+               if (chunk_equals(subject_serial, chunk_skip_zero(serial)))
                {
-                       DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
-                                &revocation, TRUE, crl_reason_names, reason);
                        if (reason != CRL_REASON_CERTIFICATE_HOLD)
                        {
                                *valid = VALIDATION_REVOKED;
@@ -413,6 +513,9 @@ static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
                                /* if the cert is on hold, a newer CRL might not contain it */
                                *valid = VALIDATION_ON_HOLD;
                        }
+                       is_crl_valid(cand, now, cache);
+                       DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
+                                &revocation, TRUE, crl_reason_names, reason);
                        enumerator->destroy(enumerator);
                        DESTROY_IF(best);
                        return cand;
@@ -425,18 +528,12 @@ static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
        {
                DESTROY_IF(best);
                best = cand;
-               if (best->get_validity(best, NULL, NULL, &valid_until))
+               if (is_crl_valid(best, now, cache))
                {
-                       DBG1(DBG_CFG, "  crl is valid: until %T", &valid_until, FALSE);
                        *valid = VALIDATION_GOOD;
-                       if (cache)
-                       {       /* we cache non-stale crls only, as a stale crls are refetched */
-                               lib->credmgr->cache_cert(lib->credmgr, best);
-                       }
                }
                else
                {
-                       DBG1(DBG_CFG, "  crl is stale: since %T", &valid_until, FALSE);
                        *valid = VALIDATION_STALE;
                }
        }
@@ -452,8 +549,8 @@ static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
  * Find or fetch a certificate for a given crlIssuer
  */
 static cert_validation_t find_crl(x509_t *subject, identification_t *issuer,
-                                                                 auth_cfg_t *auth, crl_t *base,
-                                                                 certificate_t **best, bool *uri_found)
+                                                                 crl_t *base, certificate_t **best,
+                                                                 bool *uri_found)
 {
        cert_validation_t valid = VALIDATION_SKIPPED;
        enumerator_t *enumerator;
@@ -466,8 +563,7 @@ static cert_validation_t find_crl(x509_t *subject, identification_t *issuer,
        while (enumerator->enumerate(enumerator, &current))
        {
                current->get_ref(current);
-               *best = get_better_crl(current, *best, subject, &valid,
-                                                          auth, FALSE, base);
+               *best = get_better_crl(current, *best, subject, &valid, FALSE, base);
                if (*best && valid != VALIDATION_STALE)
                {
                        DBG1(DBG_CFG, "  using cached crl");
@@ -495,7 +591,7 @@ static cert_validation_t find_crl(x509_t *subject, identification_t *issuer,
                                        continue;
                                }
                                *best = get_better_crl(current, *best, subject,
-                                                                          &valid, auth, TRUE, base);
+                                                                          &valid, TRUE, base);
                                if (*best && valid != VALIDATION_STALE)
                                {
                                        break;
@@ -508,13 +604,38 @@ static cert_validation_t find_crl(x509_t *subject, identification_t *issuer,
 }
 
 /**
+ * Check if the issuer of the given CRL matches
+ */
+static bool check_issuer(certificate_t *crl, x509_t *issuer, x509_cdp_t *cdp)
+{
+       certificate_t *cissuer = (certificate_t*)issuer;
+       identification_t *id;
+       chunk_t chunk;
+       bool matches = FALSE;
+
+       if (cdp->issuer)
+       {
+               return crl->has_issuer(crl, cdp->issuer);
+       }
+       /* check SKI/AKI first, but fall back to DN matching */
+       chunk = issuer->get_subjectKeyIdentifier(issuer);
+       if (chunk.len)
+       {
+               id = identification_create_from_encoding(ID_KEY_ID, chunk);
+               matches = crl->has_issuer(crl, id);
+               id->destroy(id);
+       }
+       return matches || crl->has_issuer(crl, cissuer->get_subject(cissuer));
+}
+
+/**
  * Look for a delta CRL for a given base CRL
  */
 static cert_validation_t check_delta_crl(x509_t *subject, x509_t *issuer,
-                                       crl_t *base, cert_validation_t base_valid, auth_cfg_t *auth)
+                                                                       crl_t *base, cert_validation_t base_valid)
 {
        cert_validation_t valid = VALIDATION_SKIPPED;
-       certificate_t *best = NULL, *current;
+       certificate_t *best = NULL, *current, *cissuer = (certificate_t*)issuer;
        enumerator_t *enumerator;
        identification_t *id;
        x509_cdp_t *cdp;
@@ -526,7 +647,7 @@ static cert_validation_t check_delta_crl(x509_t *subject, x509_t *issuer,
        if (chunk.len)
        {
                id = identification_create_from_encoding(ID_KEY_ID, chunk);
-               valid = find_crl(subject, id, auth, base, &best, &uri);
+               valid = find_crl(subject, id, base, &best, &uri);
                id->destroy(id);
        }
 
@@ -537,7 +658,7 @@ static cert_validation_t check_delta_crl(x509_t *subject, x509_t *issuer,
        {
                if (cdp->issuer)
                {
-                       valid = find_crl(subject, cdp->issuer, auth, base, &best, &uri);
+                       valid = find_crl(subject, cdp->issuer, base, &best, &uri);
                }
        }
        enumerator->destroy(enumerator);
@@ -550,16 +671,16 @@ static cert_validation_t check_delta_crl(x509_t *subject, x509_t *issuer,
                current = fetch_crl(cdp->uri);
                if (current)
                {
-                       if (cdp->issuer && !current->has_issuer(current, cdp->issuer))
+                       if (!check_issuer(current, issuer, cdp))
                        {
                                DBG1(DBG_CFG, "issuer of fetched delta CRL '%Y' does not match "
-                                        "certificates CRL issuer '%Y'",
-                                        current->get_issuer(current), cdp->issuer);
+                                        "certificate's %sissuer '%Y'",
+                                        current->get_issuer(current), cdp->issuer ? "CRL " : "",
+                                        cdp->issuer ?: cissuer->get_subject(cissuer));
                                current->destroy(current);
                                continue;
                        }
-                       best = get_better_crl(current, best, subject, &valid,
-                                                                 auth, TRUE, base);
+                       best = get_better_crl(current, best, subject, &valid, TRUE, base);
                        if (best && valid != VALIDATION_STALE)
                        {
                                break;
@@ -576,7 +697,6 @@ static cert_validation_t check_delta_crl(x509_t *subject, x509_t *issuer,
        return base_valid;
 }
 
-
 /**
  * validate a x509 certificate using CRL
  */
@@ -584,7 +704,7 @@ static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
                                                                   auth_cfg_t *auth)
 {
        cert_validation_t valid = VALIDATION_SKIPPED;
-       certificate_t *best = NULL;
+       certificate_t *best = NULL, *cissuer = (certificate_t*)issuer;
        identification_t *id;
        x509_cdp_t *cdp;
        bool uri_found = FALSE;
@@ -597,7 +717,7 @@ static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
        if (chunk.len)
        {
                id = identification_create_from_encoding(ID_KEY_ID, chunk);
-               valid = find_crl(subject, id, auth, NULL, &best, &uri_found);
+               valid = find_crl(subject, id, NULL, &best, &uri_found);
                id->destroy(id);
        }
 
@@ -608,8 +728,7 @@ static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
        {
                if (cdp->issuer)
                {
-                       valid = find_crl(subject, cdp->issuer, auth, NULL,
-                                                        &best, &uri_found);
+                       valid = find_crl(subject, cdp->issuer, NULL, &best, &uri_found);
                }
        }
        enumerator->destroy(enumerator);
@@ -624,16 +743,17 @@ static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
                        current = fetch_crl(cdp->uri);
                        if (current)
                        {
-                               if (cdp->issuer && !current->has_issuer(current, cdp->issuer))
+                               if (!check_issuer(current, issuer, cdp))
                                {
                                        DBG1(DBG_CFG, "issuer of fetched CRL '%Y' does not match "
-                                                "certificates CRL issuer '%Y'",
-                                                current->get_issuer(current), cdp->issuer);
+                                                "certificate's %sissuer '%Y'",
+                                                current->get_issuer(current), cdp->issuer ? "CRL " : "",
+                                                cdp->issuer ?: cissuer->get_subject(cissuer));
                                        current->destroy(current);
                                        continue;
                                }
                                best = get_better_crl(current, best, subject, &valid,
-                                                                         auth, TRUE, NULL);
+                                                                         TRUE, NULL);
                                if (best && valid != VALIDATION_STALE)
                                {
                                        break;
@@ -646,7 +766,7 @@ static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
        /* look for delta CRLs */
        if (best && (valid == VALIDATION_GOOD || valid == VALIDATION_STALE))
        {
-               valid = check_delta_crl(subject, issuer, (crl_t*)best, valid, auth);
+               valid = check_delta_crl(subject, issuer, (crl_t*)best, valid);
        }
 
        /* an uri was found, but no result. switch validation state to failed */
@@ -654,18 +774,15 @@ static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
        {
                valid = VALIDATION_FAILED;
        }
-       if (auth)
+       if (valid == VALIDATION_SKIPPED)
+       {       /* if we skipped CRL validation, we use the result of OCSP for
+                * constraint checking */
+               auth->add(auth, AUTH_RULE_CRL_VALIDATION,
+                                 auth->get(auth, AUTH_RULE_OCSP_VALIDATION));
+       }
+       else
        {
-               if (valid == VALIDATION_SKIPPED)
-               {       /* if we skipped CRL validation, we use the result of OCSP for
-                        * constraint checking */
-                       auth->add(auth, AUTH_RULE_CRL_VALIDATION,
-                                         auth->get(auth, AUTH_RULE_OCSP_VALIDATION));
-               }
-               else
-               {
-                       auth->add(auth, AUTH_RULE_CRL_VALIDATION, valid);
-               }
+               auth->add(auth, AUTH_RULE_CRL_VALIDATION, valid);
        }
        DESTROY_IF(best);
        return valid;
@@ -676,50 +793,72 @@ METHOD(cert_validator_t, validate, bool,
        certificate_t *issuer, bool online, u_int pathlen, bool anchor,
        auth_cfg_t *auth)
 {
-       if (subject->get_type(subject) == CERT_X509 &&
-               issuer->get_type(issuer) == CERT_X509 &&
-               online)
+       if (online && (this->enable_ocsp || this->enable_crl) &&
+               subject->get_type(subject) == CERT_X509 &&
+               issuer->get_type(issuer) == CERT_X509)
        {
                DBG1(DBG_CFG, "checking certificate status of \"%Y\"",
                                           subject->get_subject(subject));
-               switch (check_ocsp((x509_t*)subject, (x509_t*)issuer,
-                                                  pathlen ? NULL : auth))
+
+               if (this->enable_ocsp)
                {
-                       case VALIDATION_GOOD:
-                               DBG1(DBG_CFG, "certificate status is good");
-                               return TRUE;
-                       case VALIDATION_REVOKED:
-                       case VALIDATION_ON_HOLD:
-                               /* has already been logged */
-                               return FALSE;
-                       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:
-                               DBG1(DBG_CFG, "ocsp check failed, fallback to crl");
-                               break;
+                       switch (check_ocsp((x509_t*)subject, (x509_t*)issuer, auth))
+                       {
+                               case VALIDATION_GOOD:
+                                       DBG1(DBG_CFG, "certificate status is good");
+                                       return TRUE;
+                               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");
+                                       break;
+                               case VALIDATION_STALE:
+                                       DBG1(DBG_CFG, "ocsp information stale, fallback to crl");
+                                       break;
+                               case VALIDATION_FAILED:
+                                       DBG1(DBG_CFG, "ocsp check failed, fallback to crl");
+                                       break;
+                       }
                }
-               switch (check_crl((x509_t*)subject, (x509_t*)issuer,
-                                                 pathlen ? NULL : auth))
+               else
                {
-                       case VALIDATION_GOOD:
-                               DBG1(DBG_CFG, "certificate status is good");
-                               return TRUE;
-                       case VALIDATION_REVOKED:
-                       case VALIDATION_ON_HOLD:
-                               /* has already been logged */
-                               return FALSE;
-                       case VALIDATION_FAILED:
-                       case VALIDATION_SKIPPED:
-                               DBG1(DBG_CFG, "certificate status is not available");
-                               break;
-                       case VALIDATION_STALE:
-                               DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
-                               break;
+                       auth->add(auth, AUTH_RULE_OCSP_VALIDATION, VALIDATION_SKIPPED);
                }
+
+               if (this->enable_crl)
+               {
+                       switch (check_crl((x509_t*)subject, (x509_t*)issuer, auth))
+                       {
+                               case VALIDATION_GOOD:
+                                       DBG1(DBG_CFG, "certificate status is good");
+                                       return TRUE;
+                               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:
+                                       DBG1(DBG_CFG, "certificate status is not available");
+                                       break;
+                               case VALIDATION_STALE:
+                                       DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
+                                       break;
+                       }
+               }
+               else
+               {
+                       auth->add(auth, AUTH_RULE_CRL_VALIDATION,
+                                         auth->get(auth, AUTH_RULE_OCSP_VALIDATION));
+               }
+
+               lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_VALIDATION_FAILED,
+                                                               subject);
        }
        return TRUE;
 }
@@ -742,7 +881,19 @@ revocation_validator_t *revocation_validator_create()
                        .validator.validate = _validate,
                        .destroy = _destroy,
                },
+               .enable_ocsp = lib->settings->get_bool(lib->settings,
+                                                       "%s.plugins.revocation.enable_ocsp", TRUE, lib->ns),
+               .enable_crl  = lib->settings->get_bool(lib->settings,
+                                                       "%s.plugins.revocation.enable_crl",  TRUE, lib->ns),
        );
 
+       if (!this->enable_ocsp)
+       {
+               DBG1(DBG_LIB, "all OCSP validation disabled");
+       }
+       if (!this->enable_crl)
+       {
+               DBG1(DBG_LIB, "all CRL validation disabled");
+       }
        return &this->public;
 }