revocation: Set defaults if CRL/OCSP checking is disabled in config
[strongswan.git] / src / libstrongswan / plugins / revocation / revocation_validator.c
index fdcb990..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
@@ -15,6 +16,8 @@
  * for more details.
  */
 
+#include <time.h>
+
 #include "revocation_validator.h"
 
 #include <utils/debug.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;
+
 };
 
 /**
@@ -340,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;
@@ -404,6 +415,45 @@ 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,
@@ -411,9 +461,9 @@ static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
                                        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)
@@ -441,14 +491,19 @@ 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;
+       }
 
+       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;
@@ -458,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;
@@ -470,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;
                }
        }
@@ -552,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)
 {
        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;
@@ -594,11 +671,12 @@ 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;
                        }
@@ -626,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;
@@ -665,11 +743,12 @@ 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;
                                }
@@ -695,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;
@@ -717,54 +793,70 @@ 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 */
-                               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_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 */
-                               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;
+                       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);
        }
@@ -789,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;
 }