Check for OpenSSL error code for < 0.9.8
[shibboleth/cpp-xmltooling.git] / xmltooling / security / impl / PKIXPathValidator.cpp
index a142aa0..3a86057 100644 (file)
 #include "util/Threads.h"
 #include "util/XMLHelper.h"
 
+#include <memory>
 #include <algorithm>
 #include <fstream>
 #include <openssl/x509_vfy.h>
 #include <openssl/x509v3.h>
 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
+#include <xercesc/util/XMLUniDefs.hpp>
 
 using namespace xmltooling::logging;
 using namespace xmltooling;
@@ -49,8 +51,11 @@ using namespace std;
 namespace {
     static int XMLTOOL_DLLLOCAL error_callback(int ok, X509_STORE_CTX* ctx)
     {
-        if (!ok)
-            Category::getInstance("OpenSSL").error("path validation failure: %s", X509_verify_cert_error_string(ctx->error));
+        if (!ok) {
+            Category::getInstance("OpenSSL").error(
+                "path validation failure at depth(%d): %s", ctx->error_depth, X509_verify_cert_error_string(ctx->error)
+                );
+        }
         return ok;
     }
 
@@ -334,20 +339,8 @@ bool PKIXPathValidator::validate(X509* EE, STACK_OF(X509)* untrusted, const Path
         set<string> crlissuers;
         time_t now = time(nullptr);
 
-        const vector<XSECCryptoX509CRL*>& crls = pkixParams->getCRLs();
-        for (vector<XSECCryptoX509CRL*>::const_iterator j=crls.begin(); j!=crls.end(); ++j) {
-            if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL &&
-                (X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()), &now) > 0)) {
-                // owned by store
-                X509_STORE_add_crl(store, X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()));
-                string crlissuer(X509_NAME_to_string(X509_CRL_get_issuer(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL())));
-                if (!crlissuer.empty()) {
-                    m_log.debug("added CRL issued by (%s)", crlissuer.c_str());
-                    crlissuers.insert(crlissuer);
-                }
-            }
-        }
-
+        // Pull CRLs from external CDP first, since an attacker is likely to stick an old but valid CRL into
+        // the signature.
         for (int i = 0; i < sk_X509_num(untrusted); ++i) {
             X509 *cert = sk_X509_value(untrusted, i);
             string crlissuer(X509_NAME_to_string(X509_get_issuer_name(cert)));
@@ -355,7 +348,6 @@ bool PKIXPathValidator::validate(X509* EE, STACK_OF(X509)* untrusted, const Path
                // We already have a CRL for this cert, so skip CRLDP processing for this one.
                continue;
             }
-
             bool foundUsableCDP = false;
             STACK_OF(DIST_POINT)* dps = (STACK_OF(DIST_POINT)*)X509_get_ext_d2i(cert, NID_crl_distribution_points, nullptr, nullptr);
             for (int ii = 0; !foundUsableCDP && ii < sk_DIST_POINT_num(dps); ++ii) {
@@ -364,12 +356,8 @@ bool PKIXPathValidator::validate(X509* EE, STACK_OF(X509)* untrusted, const Path
                     continue;
                 for (int iii = 0; !foundUsableCDP && iii < sk_GENERAL_NAME_num(dp->distpoint->name.fullname); ++iii) {
                     GENERAL_NAME* gen = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, iii);
-                    // Only consider HTTP URIs, and stop after the first one we find.
-#ifdef HAVE_STRCASECMP
-                    if (gen->type == GEN_URI && (!strncasecmp((const char*)gen->d.ia5->data, "http:", 5))) {
-#else
-                    if (gen->type == GEN_URI && (!strnicmp((const char*)gen->d.ia5->data, "http:", 5))) {
-#endif
+                    // Only consider URIs, and stop after the first one we find.
+                    if (gen->type == GEN_URI) {
                         const char* cdpuri = (const char*)gen->d.ia5->data;
                         auto_ptr<XSECCryptoX509CRL> crl(getRemoteCRLs(cdpuri));
                         if (crl.get() && crl->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL &&
@@ -386,6 +374,23 @@ bool PKIXPathValidator::validate(X509* EE, STACK_OF(X509)* untrusted, const Path
             sk_DIST_POINT_free(dps);
         }
 
+        // Pick up any valid CRLs inline.
+        const vector<XSECCryptoX509CRL*>& crls = pkixParams->getCRLs();
+        for (vector<XSECCryptoX509CRL*>::const_iterator j=crls.begin(); j!=crls.end(); ++j) {
+            if ((*j)->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL &&
+                (X509_cmp_time(X509_CRL_get_nextUpdate(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()), &now) > 0)) {
+                string crlissuer(X509_NAME_to_string(X509_CRL_get_issuer(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL())));
+                if (crlissuer.empty() || crlissuers.count(crlissuer)) {
+                   // We already have a CRL for this cert, so skip this one.
+                   continue;
+                }
+                m_log.debug("added CRL issued by (%s)", crlissuer.c_str());
+                crlissuers.insert(crlissuer);
+                // owned by store
+                X509_STORE_add_crl(store, X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(*j)->getOpenSSLX509CRL()));
+            }
+        }
+
         // Do a second pass verify with CRLs in place.
         if (pkixParams->getRevocationChecking() == PKIXPathValidatorParams::REVOCATION_FULLCHAIN)
             X509_STORE_CTX_set_flags(&ctx, X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
@@ -398,17 +403,21 @@ bool PKIXPathValidator::validate(X509* EE, STACK_OF(X509)* untrusted, const Path
 #endif
     }
 
+    if (ret == 1) {
+        m_log.debug("successfully validated certificate chain");
+    }
+#if defined(X509_V_ERR_NO_EXPLICIT_POLICY) && (OPENSSL_VERSION_NUMBER < 0x10000000L)
+    else if (X509_STORE_CTX_get_error(&ctx) == X509_V_ERR_NO_EXPLICIT_POLICY && !pkixParams->isPolicyMappingInhibited()) {
+        m_log.warn("policy mapping requires OpenSSL 1.0.0 or later");
+    }
+#endif
+
     // Clean up...
     X509_STORE_CTX_cleanup(&ctx);
     X509_STORE_free(store);
     sk_X509_free(CAstack);
 
-    if (ret==1) {
-        m_log.debug("successfully validated certificate chain");
-        return true;
-    }
-
-    return false;
+    return (ret == 1);
 }
 
 XSECCryptoX509CRL* PKIXPathValidator::getRemoteCRLs(const char* cdpuri) const