revocation: Set defaults if CRL/OCSP checking is disabled in config
[strongswan.git] / src / libstrongswan / plugins / revocation / revocation_validator.c
index 7984299..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>
@@ -38,12 +41,12 @@ struct private_revocation_validator_t {
        revocation_validator_t public;
 
        /**
-        * Enable OCSP fetching
+        * Enable OCSP validation
         */
        bool enable_ocsp;
 
        /**
-        * Enable CRL fetching
+        * Enable CRL validation
         */
        bool enable_crl;
 
@@ -351,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;
@@ -417,11 +417,11 @@ 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, bool cache)
+static bool is_crl_valid(certificate_t *crl, time_t now, bool cache)
 {
        time_t valid_until;
 
-       if (crl->get_validity(crl, NULL, NULL, &valid_until))
+       if (crl->get_validity(crl, &now, NULL, &valid_until))
        {
                DBG1(DBG_CFG, "  crl is valid: until %T", &valid_until, FALSE);
                if (cache)
@@ -435,6 +435,25 @@ static bool is_crl_valid(certificate_t *crl, bool cache)
 }
 
 /**
+ * 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,
@@ -442,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;
+       time_t now, revocation;
        crl_reason_t reason;
-       chunk_t serial;
+       chunk_t subject_serial, serial;
        crl_t *crl = (crl_t*)cand;
 
        if (base)
@@ -472,11 +491,18 @@ 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)))
                {
                        if (reason != CRL_REASON_CERTIFICATE_HOLD)
                        {
@@ -487,7 +513,7 @@ 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, cache);
+                       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);
@@ -502,7 +528,7 @@ static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
        {
                DESTROY_IF(best);
                best = cand;
-               if (is_crl_valid(best, cache))
+               if (is_crl_valid(best, now, cache))
                {
                        *valid = VALIDATION_GOOD;
                }
@@ -578,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;
@@ -620,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;
                        }
@@ -652,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;
@@ -691,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;
                                }
@@ -721,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;
@@ -743,17 +793,16 @@ 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));
 
                if (this->enable_ocsp)
                {
-                       switch (check_ocsp((x509_t*)subject, (x509_t*)issuer,
-                                                          pathlen ? NULL : auth))
+                       switch (check_ocsp((x509_t*)subject, (x509_t*)issuer, auth))
                        {
                                case VALIDATION_GOOD:
                                        DBG1(DBG_CFG, "certificate status is good");
@@ -775,11 +824,14 @@ METHOD(cert_validator_t, validate, bool,
                                        break;
                        }
                }
+               else
+               {
+                       auth->add(auth, AUTH_RULE_OCSP_VALIDATION, VALIDATION_SKIPPED);
+               }
 
                if (this->enable_crl)
                {
-                       switch (check_crl((x509_t*)subject, (x509_t*)issuer,
-                                                         pathlen ? NULL : auth))
+                       switch (check_crl((x509_t*)subject, (x509_t*)issuer, auth))
                        {
                                case VALIDATION_GOOD:
                                        DBG1(DBG_CFG, "certificate status is good");
@@ -799,6 +851,11 @@ METHOD(cert_validator_t, validate, bool,
                                        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);
@@ -832,12 +889,11 @@ revocation_validator_t *revocation_validator_create()
 
        if (!this->enable_ocsp)
        {
-               DBG1(DBG_LIB, "all OCSP fetching disabled");
+               DBG1(DBG_LIB, "all OCSP validation disabled");
        }
        if (!this->enable_crl)
        {
-               DBG1(DBG_LIB, "all CRL fetching disabled");
+               DBG1(DBG_LIB, "all CRL validation disabled");
        }
-
        return &this->public;
 }