revocation: Set defaults if CRL/OCSP checking is disabled in config
[strongswan.git] / src / libstrongswan / plugins / revocation / revocation_validator.c
index e7802d4..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;
+
 };
 
 /**
@@ -58,7 +72,12 @@ static certificate_t *fetch_ocsp(char *url, certificate_t *subject,
                return NULL;
        }
 
-       send = request->get_encoding(request);
+       if (!request->get_encoding(request, CERT_ASN1_DER, &send))
+       {
+               DBG1(DBG_CFG, "encoding ocsp request failed");
+               request->destroy(request);
+               return NULL;
+       }
        request->destroy(request);
 
        DBG1(DBG_CFG, "  requesting ocsp status from '%s' ...", url);
@@ -88,35 +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)
+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;
-       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);
+       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, NULL))
+       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));
+                                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;
 }
 
@@ -124,7 +200,8 @@ static bool verify_ocsp(ocsp_response_t *response)
  * 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, 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;
@@ -134,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))
+       if (!verify_ocsp(response, &issuer->interface))
        {
-               DBG1(DBG_CFG, "ocsp response verification failed");
                cand->destroy(cand);
                return best;
        }
@@ -227,7 +303,7 @@ static cert_validation_t check_ocsp(x509_t *subject, x509_t *issuer,
        /* derive the authorityKeyIdentifier from the issuer's public key */
        current = &issuer->interface;
        public = current->get_public_key(current);
-       if (public && public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
+       if (public && public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk))
        {
                keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
        }
@@ -278,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;
@@ -305,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)
        {
@@ -328,7 +401,7 @@ static bool verify_crl(certificate_t *crl)
                                                                                KEY_ANY, crl->get_issuer(crl), FALSE);
        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));
@@ -342,16 +415,74 @@ static bool verify_crl(certificate_t *crl)
 }
 
 /**
+ * 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, x509_t *issuer, cert_validation_t *valid, bool cache)
+                                       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;
-       crl_t *crl;
+       chunk_t subject_serial, serial;
+       crl_t *crl = (crl_t*)cand;
+
+       if (base)
+       {
+               if (!crl->is_delta_crl(crl, &serial) ||
+                       !chunk_equals(serial, base->get_serial(base)))
+               {
+                       cand->destroy(cand);
+                       return best;
+               }
+       }
+       else
+       {
+               if (crl->is_delta_crl(crl, NULL))
+               {
+                       cand->destroy(cand);
+                       return best;
+               }
+       }
 
        /* check CRL signature */
        if (!verify_crl(cand))
@@ -360,16 +491,31 @@ static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
                cand->destroy(cand);
                return best;
        }
+       now = time(NULL);
+       if (is_crl_not_valid_yet(cand, now))
+       {
+               cand->destroy(cand);
+               return best;
+       }
 
-       crl = (crl_t*)cand;
+       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)))
                {
+                       if (reason != CRL_REASON_CERTIFICATE_HOLD)
+                       {
+                               *valid = VALIDATION_REVOKED;
+                       }
+                       else
+                       {
+                               /* 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);
-                       *valid = VALIDATION_REVOKED;
                        enumerator->destroy(enumerator);
                        DESTROY_IF(best);
                        return cand;
@@ -382,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;
                }
        }
@@ -406,79 +546,214 @@ static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
 }
 
 /**
- * validate a x509 certificate using CRL
+ * Find or fetch a certificate for a given crlIssuer
  */
-static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
-                                                                  auth_cfg_t *auth)
+static cert_validation_t find_crl(x509_t *subject, identification_t *issuer,
+                                                                 crl_t *base, certificate_t **best,
+                                                                 bool *uri_found)
 {
        cert_validation_t valid = VALIDATION_SKIPPED;
-       identification_t *keyid = NULL;
-       certificate_t *best = NULL;
+       enumerator_t *enumerator;
        certificate_t *current;
-       public_key_t *public;
+       char *uri;
+
+       /* find a cached (delta) crl */
+       enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
+                                                                               CERT_X509_CRL, KEY_ANY, issuer, FALSE);
+       while (enumerator->enumerate(enumerator, &current))
+       {
+               current->get_ref(current);
+               *best = get_better_crl(current, *best, subject, &valid, FALSE, base);
+               if (*best && valid != VALIDATION_STALE)
+               {
+                       DBG1(DBG_CFG, "  using cached crl");
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       /* fallback to fetching crls from credential sets cdps */
+       if (!base && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
+       {
+               enumerator = lib->credmgr->create_cdp_enumerator(lib->credmgr,
+                                                                                                                CERT_X509_CRL, issuer);
+               while (enumerator->enumerate(enumerator, &uri))
+               {
+                       *uri_found = TRUE;
+                       current = fetch_crl(uri);
+                       if (current)
+                       {
+                               if (!current->has_issuer(current, issuer))
+                               {
+                                       DBG1(DBG_CFG, "issuer of fetched CRL '%Y' does not match CRL "
+                                                "issuer '%Y'", current->get_issuer(current), issuer);
+                                       current->destroy(current);
+                                       continue;
+                               }
+                               *best = get_better_crl(current, *best, subject,
+                                                                          &valid, TRUE, base);
+                               if (*best && valid != VALIDATION_STALE)
+                               {
+                                       break;
+                               }
+                       }
+               }
+               enumerator->destroy(enumerator);
+       }
+       return valid;
+}
+
+/**
+ * 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)
+{
+       cert_validation_t valid = VALIDATION_SKIPPED;
+       certificate_t *best = NULL, *current, *cissuer = (certificate_t*)issuer;
        enumerator_t *enumerator;
+       identification_t *id;
+       x509_cdp_t *cdp;
        chunk_t chunk;
-       char *uri = NULL;
+       bool uri;
 
-       /* derive the authorityKeyIdentifier from the issuer's public key */
-       current = &issuer->interface;
-       public = current->get_public_key(current);
-       if (public && public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
+       /* find cached delta CRL via subjectKeyIdentifier */
+       chunk = issuer->get_subjectKeyIdentifier(issuer);
+       if (chunk.len)
        {
-               keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
+               id = identification_create_from_encoding(ID_KEY_ID, chunk);
+               valid = find_crl(subject, id, base, &best, &uri);
+               id->destroy(id);
+       }
 
-               /* find a cached crl by authorityKeyIdentifier */
-               enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
-                                                                               CERT_X509_CRL, KEY_ANY, keyid, FALSE);
-               while (enumerator->enumerate(enumerator, &current))
+       /* find delta CRL by CRLIssuer */
+       enumerator = subject->create_crl_uri_enumerator(subject);
+       while (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED &&
+                  enumerator->enumerate(enumerator, &cdp))
+       {
+               if (cdp->issuer)
+               {
+                       valid = find_crl(subject, cdp->issuer, base, &best, &uri);
+               }
+       }
+       enumerator->destroy(enumerator);
+
+       /* fetch from URIs found in Freshest CRL extension */
+       enumerator = base->create_delta_crl_uri_enumerator(base);
+       while (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED &&
+                  enumerator->enumerate(enumerator, &cdp))
+       {
+               current = fetch_crl(cdp->uri);
+               if (current)
                {
-                       current->get_ref(current);
-                       best = get_better_crl(current, best, subject, issuer,
-                                                                 &valid, FALSE);
+                       if (!check_issuer(current, issuer, cdp))
+                       {
+                               DBG1(DBG_CFG, "issuer of fetched delta CRL '%Y' does not match "
+                                        "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, TRUE, base);
                        if (best && valid != VALIDATION_STALE)
                        {
-                               DBG1(DBG_CFG, "  using cached crl");
                                break;
                        }
                }
-               enumerator->destroy(enumerator);
+       }
+       enumerator->destroy(enumerator);
+
+       if (best)
+       {
+               best->destroy(best);
+               return valid;
+       }
+       return base_valid;
+}
+
+/**
+ * validate a x509 certificate using CRL
+ */
+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, *cissuer = (certificate_t*)issuer;
+       identification_t *id;
+       x509_cdp_t *cdp;
+       bool uri_found = FALSE;
+       certificate_t *current;
+       enumerator_t *enumerator;
+       chunk_t chunk;
+
+       /* use issuers subjectKeyIdentifier to find a cached CRL / fetch from CDP */
+       chunk = issuer->get_subjectKeyIdentifier(issuer);
+       if (chunk.len)
+       {
+               id = identification_create_from_encoding(ID_KEY_ID, chunk);
+               valid = find_crl(subject, id, NULL, &best, &uri_found);
+               id->destroy(id);
+       }
 
-               /* fallback to fetching crls from credential sets cdps */
-               if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
+       /* find a cached CRL or fetch via configured CDP via CRLIssuer */
+       enumerator = subject->create_crl_uri_enumerator(subject);
+       while (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED &&
+                  enumerator->enumerate(enumerator, &cdp))
+       {
+               if (cdp->issuer)
                {
-                       enumerator = lib->credmgr->create_cdp_enumerator(lib->credmgr,
-                                                                                                               CERT_X509_CRL, keyid);
-                       while (enumerator->enumerate(enumerator, &uri))
-                       {
-                               current = fetch_crl(uri);
-                               if (current)
-                               {
-                                       best = get_better_crl(current, best, subject, issuer,
-                                                                                 &valid, TRUE);
-                                       if (best && valid != VALIDATION_STALE)
-                                       {
-                                               break;
-                                       }
-                               }
-                       }
-                       enumerator->destroy(enumerator);
+                       valid = find_crl(subject, cdp->issuer, NULL, &best, &uri_found);
                }
-               keyid->destroy(keyid);
        }
-       DESTROY_IF(public);
+       enumerator->destroy(enumerator);
 
-       /* fallback to fetching crls from cdps from subject's certificate */
+       /* fallback to fetching CRLs from CDPs found in subjects certificate */
        if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
        {
                enumerator = subject->create_crl_uri_enumerator(subject);
-
-               while (enumerator->enumerate(enumerator, &uri))
+               while (enumerator->enumerate(enumerator, &cdp))
                {
-                       current = fetch_crl(uri);
+                       uri_found = TRUE;
+                       current = fetch_crl(cdp->uri);
                        if (current)
                        {
-                               best = get_better_crl(current, best, subject, issuer,
-                                                                         &valid, TRUE);
+                               if (!check_issuer(current, issuer, cdp))
+                               {
+                                       DBG1(DBG_CFG, "issuer of fetched CRL '%Y' does not match "
+                                                "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,
+                                                                         TRUE, NULL);
                                if (best && valid != VALIDATION_STALE)
                                {
                                        break;
@@ -488,23 +763,26 @@ static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
                enumerator->destroy(enumerator);
        }
 
+       /* look for delta CRLs */
+       if (best && (valid == VALIDATION_GOOD || valid == VALIDATION_STALE))
+       {
+               valid = check_delta_crl(subject, issuer, (crl_t*)best, valid);
+       }
+
        /* an uri was found, but no result. switch validation state to failed */
-       if (valid == VALIDATION_SKIPPED && uri)
+       if (valid == VALIDATION_SKIPPED && uri_found)
        {
                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;
@@ -512,48 +790,75 @@ static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
 
 METHOD(cert_validator_t, validate, bool,
        private_revocation_validator_t *this, certificate_t *subject,
-       certificate_t *issuer, bool online, int pathlen, auth_cfg_t *auth)
+       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, auth))
+
+               if (this->enable_ocsp)
                {
-                       case VALIDATION_GOOD:
-                               DBG1(DBG_CFG, "certificate status is good");
-                               return TRUE;
-                       case VALIDATION_REVOKED:
-                               /* 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, auth))
+               else
                {
-                       case VALIDATION_GOOD:
-                               DBG1(DBG_CFG, "certificate status is good");
-                               return TRUE;
-                       case VALIDATION_REVOKED:
-                               /* 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;
 }
@@ -576,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;
 }