Handle null criteria when trusted names are set
[shibboleth/cpp-xmltooling.git] / xmltooling / security / impl / AbstractPKIXTrustEngine.cpp
index 7cd88eb..5d7a598 100644 (file)
@@ -1,17 +1,21 @@
-/*
- *  Copyright 2001-2010 Internet2
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the
+ * License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
  */
 
 /**
@@ -253,9 +257,12 @@ namespace {
         X509* EE,
         STACK_OF(X509)* untrusted,
         AbstractPKIXTrustEngine::PKIXValidationInfoIterator* pkixInfo,
-               bool useCRL,
+        bool useCRL,
         bool fullCRLChain,
-        const vector<XSECCryptoX509CRL*>* inlineCRLs=nullptr
+        const vector<XSECCryptoX509CRL*>* inlineCRLs=nullptr,
+        bool policyMappingInhibit=false,
+        bool anyPolicyInhibit=false,
+        const set<string>* policyOIDs=nullptr
         )
     {
         Category& log=Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine");
@@ -269,6 +276,62 @@ namespace {
             log_openssl();
             return false;
         }
+
+        // PKIX policy checking (cf. RFCs 3280/5280 section 6)
+        if (policyMappingInhibit || anyPolicyInhibit || (policyOIDs && !policyOIDs->empty())) {
+#if (OPENSSL_VERSION_NUMBER < 0x00908000L)
+            log.error("PKIX policy checking option is configured, but OpenSSL version is less than 0.9.8");
+            X509_STORE_free(store);
+            return false;
+#else
+            unsigned long pflags = 0;
+            X509_VERIFY_PARAM *vpm = X509_VERIFY_PARAM_new();
+            if (!vpm) {
+                log_openssl();
+                X509_STORE_free(store);
+                return false;
+            }
+
+            // populate the "user-initial-policy-set" input variable
+            if (policyOIDs && !policyOIDs->empty()) {
+                for (set<string>::const_iterator o=policyOIDs->begin(); o!=policyOIDs->end(); o++) {
+                    ASN1_OBJECT *oid = OBJ_txt2obj(o->c_str(), 1);
+                    if (oid && X509_VERIFY_PARAM_add0_policy(vpm, oid)) {
+                        log.debug("OID (%s) added to set of acceptable policies", o->c_str());
+                    }
+                    else {
+                        log_openssl();
+                        log.error("unable to parse/configure policy OID value (%s)", o->c_str());
+                        if (oid)
+                            ASN1_OBJECT_free(oid);
+                        X509_VERIFY_PARAM_free(vpm);
+                        X509_STORE_free(store);
+                        return false;
+                    }
+                }
+                // when the user has supplied at least one policy OID, he obviously wants to check
+                // for an explicit policy ("initial-explicit-policy")
+                pflags |= X509_V_FLAG_EXPLICIT_POLICY;
+            }
+
+            // "initial-policy-mapping-inhibit" input variable
+            if (policyMappingInhibit)
+                pflags |= X509_V_FLAG_INHIBIT_MAP;
+            // "initial-any-policy-inhibit" input variable
+            if (anyPolicyInhibit)
+                pflags |= X509_V_FLAG_INHIBIT_ANY;
+
+            if (!X509_VERIFY_PARAM_set_flags(vpm, pflags) || !X509_STORE_set1_param(store, vpm)) {
+                log_openssl();
+                log.error("unable to set PKIX policy checking parameters");
+                X509_VERIFY_PARAM_free(vpm);
+                X509_STORE_free(store);
+                return false;
+            }
+
+            X509_VERIFY_PARAM_free(vpm);
+#endif
+        }
     
         // This contains the state of the validate operation.
         int count=0;
@@ -376,7 +439,7 @@ namespace {
                             const char* cdpuri = (const char*)gen->d.ia5->data;
                             auto_ptr<XSECCryptoX509CRL> crl(getRemoteCRLs(cdpuri, log));
                             if (crl.get() && crl->getProviderName()==DSIGConstants::s_unicodeStrPROVOpenSSL &&
-                                (isFreshCRL(crl.get()) || (ii == sk_DIST_POINT_num(dps) && iii == sk_GENERAL_NAME_num(dp->distpoint->name.fullname)))) {
+                                (isFreshCRL(crl.get()) || (ii == sk_DIST_POINT_num(dps)-1 && iii == sk_GENERAL_NAME_num(dp->distpoint->name.fullname)-1))) {
                                 // owned by store
                                 X509_STORE_add_crl(store, X509_CRL_dup(static_cast<OpenSSLCryptoX509CRL*>(crl.get())->getOpenSSLX509CRL()));
                                 log.debug("added CRL issued by (%s)", crlissuer.c_str());
@@ -389,25 +452,13 @@ namespace {
                 sk_DIST_POINT_free(dps);
             }
 
-            if (!crlissuers.empty()) {
-                X509_STORE_set_flags(store, fullCRLChain ? (X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL) : (X509_V_FLAG_CRL_CHECK));
-                   }
-                   else {
-                           log.warn("CRL checking is enabled, but none were supplied");
-                X509_STORE_CTX_cleanup(&ctx);
-                X509_STORE_free(store);
-                sk_X509_free(CAstack);
-                return false;
-                   }
-#else
-                       log.warn("CRL checking is enabled, but OpenSSL version is too old");
-            X509_STORE_CTX_cleanup(&ctx);
-            X509_STORE_free(store);
-            sk_X509_free(CAstack);
-            return false;
-#endif
             // Do a second pass verify with CRLs in place.
+            X509_STORE_CTX_set_flags(&ctx, fullCRLChain ? (X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL) : (X509_V_FLAG_CRL_CHECK));
             ret=X509_verify_cert(&ctx);
+#else
+            log.warn("CRL checking is enabled, but OpenSSL version is too old");
+            ret = 0;
+#endif
         }
 
         // Clean up...
@@ -423,8 +474,12 @@ namespace {
         return false;
     }
 
-    static XMLCh fullCRLChain[] =              UNICODE_LITERAL_12(f,u,l,l,C,R,L,C,h,a,i,n);
-       static XMLCh checkRevocation[] =        UNICODE_LITERAL_15(c,h,e,c,k,R,e,v,o,c,a,t,i,o,n);
+    static XMLCh fullCRLChain[] =                  UNICODE_LITERAL_12(f,u,l,l,C,R,L,C,h,a,i,n);
+    static XMLCh checkRevocation[] =       UNICODE_LITERAL_15(c,h,e,c,k,R,e,v,o,c,a,t,i,o,n);
+    static XMLCh policyMappingInhibit[] =   UNICODE_LITERAL_20(p,o,l,i,c,y,M,a,p,p,i,n,g,I,n,h,i,b,i,t);
+    static XMLCh anyPolicyInhibit[] =      UNICODE_LITERAL_16(a,n,y,P,o,l,i,c,y,I,n,h,i,b,i,t);
+    static XMLCh PolicyOID[] =                     UNICODE_LITERAL_9(P,o,l,i,c,y,O,I,D);
+    static XMLCh TrustedName[] =                   UNICODE_LITERAL_11(T,r,u,s,t,e,d,N,a,m,e);
 };
 
 AbstractPKIXTrustEngine::PKIXValidationInfoIterator::PKIXValidationInfoIterator()
@@ -437,8 +492,10 @@ AbstractPKIXTrustEngine::PKIXValidationInfoIterator::~PKIXValidationInfoIterator
 
 AbstractPKIXTrustEngine::AbstractPKIXTrustEngine(const xercesc::DOMElement* e)
        : TrustEngine(e),
+               m_checkRevocation(XMLHelper::getAttrString(e, nullptr, checkRevocation)),
                m_fullCRLChain(XMLHelper::getAttrBool(e, false, fullCRLChain)),
-               m_checkRevocation(XMLHelper::getAttrString(e, nullptr, checkRevocation))
+               m_policyMappingInhibit(XMLHelper::getAttrBool(e, false, policyMappingInhibit)),
+               m_anyPolicyInhibit(XMLHelper::getAttrBool(e, false, anyPolicyInhibit))
 {
     if (m_fullCRLChain) {
         Category::getInstance(XMLTOOLING_LOGCAT".TrustEngine.PKIX").warn(
@@ -449,6 +506,20 @@ AbstractPKIXTrustEngine::AbstractPKIXTrustEngine(const xercesc::DOMElement* e)
     else if (m_checkRevocation == "fullChain") {
         m_fullCRLChain = true; // in case anything's using this
     }
+
+    xercesc::DOMElement* c = XMLHelper::getFirstChildElement(e);
+    while (c) {
+        if (c->hasChildNodes()) {
+            auto_ptr_char v(c->getTextContent());
+            if (v.get() && *v.get()) {
+                if (XMLString::equals(c->getLocalName(), PolicyOID))
+                    m_policyOIDs.insert(v.get());
+                else if (XMLString::equals(c->getLocalName(), TrustedName))
+                    m_trustedNames.insert(v.get());
+            }
+        }
+        c = XMLHelper::getNextSiblingElement(c);
+    }
 }
 
 AbstractPKIXTrustEngine::~AbstractPKIXTrustEngine()
@@ -466,10 +537,24 @@ bool AbstractPKIXTrustEngine::checkEntityNames(
     credResolver.resolve(creds,&criteria);
 
     // Build a list of acceptable names.
-    set<string> trustednames;
-    trustednames.insert(criteria.getPeerName());
-    for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred)
+    set<string> trustednames = m_trustedNames;
+    if (log.isDebugEnabled()) {
+        for (set<string>::const_iterator n=m_trustedNames.begin(); n!=m_trustedNames.end(); n++) {
+            log.debug("adding to list of trusted names (%s)", n->c_str());
+        }
+    }
+    if (criteria.getPeerName()) {
+        trustednames.insert(criteria.getPeerName());
+        log.debug("adding to list of trusted names (%s)", criteria.getPeerName());
+    }
+    for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
         trustednames.insert((*cred)->getKeyNames().begin(), (*cred)->getKeyNames().end());
+        if (log.isDebugEnabled()) {
+            for (set<string>::const_iterator n=(*cred)->getKeyNames().begin(); n!=(*cred)->getKeyNames().end(); n++) {
+                log.debug("adding to list of trusted names (%s)", n->c_str());
+            }
+        }
+    }
 
     X509_NAME* subject=X509_get_subject_name(certEE);
     if (subject) {
@@ -609,13 +694,22 @@ bool AbstractPKIXTrustEngine::validateWithCRLs(
 
     if (criteria && criteria->getPeerName() && *(criteria->getPeerName())) {
         log.debug("checking that the certificate name is acceptable");
-        if (criteria->getUsage()==Credential::UNSPECIFIED_CREDENTIAL)
+        if (criteria && criteria->getUsage()==Credential::UNSPECIFIED_CREDENTIAL)
             criteria->setUsage(Credential::SIGNING_CREDENTIAL);
         if (!checkEntityNames(certEE,credResolver,*criteria)) {
             log.error("certificate name was not acceptable");
             return false;
         }
     }
+    else if (!m_trustedNames.empty()) {
+        log.debug("checking that the certificate name is acceptable");
+        CredentialCriteria cc;
+        cc.setUsage(Credential::SIGNING_CREDENTIAL);
+        if (!checkEntityNames(certEE,credResolver,cc)) {
+            log.error("certificate name was not acceptable");
+            return false;
+        }
+    }
     
     log.debug("performing certificate path validation...");
 
@@ -627,7 +721,10 @@ bool AbstractPKIXTrustEngine::validateWithCRLs(
                                pkix.get(),
                                (m_checkRevocation=="entityOnly" || m_checkRevocation=="fullChain"),
                                (m_checkRevocation=="fullChain"),
-                               (m_checkRevocation=="entityOnly" || m_checkRevocation=="fullChain") ? inlineCRLs : nullptr
+                               (m_checkRevocation=="entityOnly" || m_checkRevocation=="fullChain") ? inlineCRLs : nullptr,
+                               m_policyMappingInhibit,
+                               m_anyPolicyInhibit,
+                               &m_policyOIDs
                                )) {
             return true;
         }