From 9b652e511bbed2698e1951e80150fd59633707f0 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Thu, 11 Aug 2011 18:56:02 +0000 Subject: [PATCH] https://issues.shibboleth.net/jira/browse/CPPXT-78 --- xmltooling/security/AbstractPKIXTrustEngine.h | 26 ++++- .../security/impl/AbstractPKIXTrustEngine.cpp | 118 +++++++++++++++++++-- 2 files changed, 129 insertions(+), 15 deletions(-) diff --git a/xmltooling/security/AbstractPKIXTrustEngine.h b/xmltooling/security/AbstractPKIXTrustEngine.h index f83dad2..2437e2d 100644 --- a/xmltooling/security/AbstractPKIXTrustEngine.h +++ b/xmltooling/security/AbstractPKIXTrustEngine.h @@ -31,6 +31,7 @@ #include #include +#include #include namespace xmltooling { @@ -51,24 +52,41 @@ namespace xmltooling { * *
    *
  • checkRevocation attribute (off, entityOnly, fullChain) + *
  • policyMappingInhibit attribute (boolean) + *
  • anyPolicyInhibit attribute (boolean) + *
  • <TrustedName> element (zero or more) + *
  • <PolicyOID> element (zero or more) *
* * @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 m_policyOIDs; + + /** A list of trusted names (subject DNs / CN attributes / subjectAltName entries). */ + std::set 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 diff --git a/xmltooling/security/impl/AbstractPKIXTrustEngine.cpp b/xmltooling/security/impl/AbstractPKIXTrustEngine.cpp index 8d838f5..25cdcb1 100644 --- a/xmltooling/security/impl/AbstractPKIXTrustEngine.cpp +++ b/xmltooling/security/impl/AbstractPKIXTrustEngine.cpp @@ -257,9 +257,12 @@ namespace { X509* EE, STACK_OF(X509)* untrusted, AbstractPKIXTrustEngine::PKIXValidationInfoIterator* pkixInfo, - bool useCRL, + bool useCRL, bool fullCRLChain, - const vector* inlineCRLs=nullptr + const vector* inlineCRLs=nullptr, + bool policyMappingInhibit=false, + bool anyPolicyInhibit=false, + const set* 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::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 trustednames; - trustednames.insert(criteria.getPeerName()); - for (vector::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) + set trustednames = m_trustedNames; + if (log.isDebugEnabled()) { + for (set::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_iterator cred = creds.begin(); cred!=creds.end(); ++cred) { trustednames.insert((*cred)->getKeyNames().begin(), (*cred)->getKeyNames().end()); + if (log.isDebugEnabled()) { + for (set::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; } -- 2.1.4