https://issues.shibboleth.net/jira/browse/CPPXT-78
authorScott Cantor <cantor.2@osu.edu>
Thu, 11 Aug 2011 18:56:02 +0000 (18:56 +0000)
committerScott Cantor <cantor.2@osu.edu>
Thu, 11 Aug 2011 18:56:02 +0000 (18:56 +0000)
xmltooling/security/AbstractPKIXTrustEngine.h
xmltooling/security/impl/AbstractPKIXTrustEngine.cpp

index f83dad2..2437e2d 100644 (file)
@@ -31,6 +31,7 @@
 #include <xmltooling/security/OpenSSLTrustEngine.h>
 #include <xmltooling/security/SignatureTrustEngine.h>
 
+#include <set>
 #include <string>
 
 namespace xmltooling {
@@ -51,24 +52,41 @@ namespace xmltooling {
          * 
          * <ul>
          *  <li>checkRevocation attribute (off, entityOnly, fullChain)
+         *  <li>policyMappingInhibit attribute (boolean)
+         *  <li>anyPolicyInhibit attribute (boolean)
+         *  <li>&lt;TrustedName&gt; element (zero or more)
+         *  <li>&lt;PolicyOID&gt; element (zero or more)
          * </ul>
          * 
          * @param e DOM to supply configuration for provider
          */
         AbstractPKIXTrustEngine(const xercesc::DOMElement* e=nullptr);
 
-               /** Controls revocation checking, currently limited to CRLs and supports "off", "entityOnly", "fullChain". */
-               std::string m_checkRevocation;
+        /** Controls revocation checking, currently limited to CRLs and supports "off", "entityOnly", "fullChain". */
+        std::string m_checkRevocation;
 
         /** Deprecated option, equivalent to checkRevocation="fullChain". */
         bool m_fullCRLChain;
-        
+
+        /** Disable policy mapping when applying PKIX policy checking. */
+        bool m_policyMappingInhibit;
+
+        /** Disallow the anyPolicy OID (2.5.29.32.0) when applying PKIX policy checking. */
+        bool m_anyPolicyInhibit;
+
+        /** A list of acceptable policy OIDs (explicit policy checking). */
+        std::set<std::string> m_policyOIDs;
+
+        /** A list of trusted names (subject DNs / CN attributes / subjectAltName entries). */
+        std::set<std::string> m_trustedNames;
+
         /**
          * Checks that either the name of the peer with the given credentials or the names
          * of the credentials match the subject or subject alternate names of the certificate.
+         * Alternatively explicit trusted names can be supplied statically via configuration.
          * 
          * @param certEE        the credential for the entity to validate
-         * @param credResolver  source of credentials
+         * @param credResolver  source of trusted credentials
          * @param criteria      criteria for selecting credentials, including the peer name
          * 
          * @return true the name check succeeds, false if not
index 8d838f5..25cdcb1 100644 (file)
@@ -257,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");
@@ -273,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;
@@ -415,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()
@@ -429,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(
@@ -441,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()
@@ -458,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) {
@@ -599,9 +692,9 @@ bool AbstractPKIXTrustEngine::validateWithCRLs(
         return false;
     }
 
-    if (criteria && criteria->getPeerName() && *(criteria->getPeerName())) {
+    if ((criteria && criteria->getPeerName() && *(criteria->getPeerName())) || !m_trustedNames.empty()) {
         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");
@@ -619,7 +712,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;
         }